mirror of
https://github.com/fish-shell/fish-shell.git
synced 2026-05-10 18:31:14 -03:00
Compare commits
285 Commits
OpenBeta_r
...
pre_whites
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5827bfd9a2 | ||
|
|
dc8cc39bc8 | ||
|
|
c9c2fc5ee3 | ||
|
|
b79854ad1a | ||
|
|
8229b17f0d | ||
|
|
4a5d02aab4 | ||
|
|
ed4e6cd3a5 | ||
|
|
7d45e6f12f | ||
|
|
0302162d8e | ||
|
|
1dfa404210 | ||
|
|
bd4551e2ef | ||
|
|
d6a56428d1 | ||
|
|
a10fa3a20b | ||
|
|
be5fde9e54 | ||
|
|
abfc7e16a1 | ||
|
|
d76f880faf | ||
|
|
21e83a881e | ||
|
|
5ba1261285 | ||
|
|
7bb844a778 | ||
|
|
8c24d49c73 | ||
|
|
5e371e8fe7 | ||
|
|
e46324ced9 | ||
|
|
5ea486b20a | ||
|
|
425afa63ce | ||
|
|
7c09a767b6 | ||
|
|
768f92303e | ||
|
|
34054fa1fb | ||
|
|
4a37d6d1f9 | ||
|
|
8dfc8625a7 | ||
|
|
e878947cb2 | ||
|
|
f56f84c6ac | ||
|
|
239d43dac4 | ||
|
|
8a63326411 | ||
|
|
01608cf062 | ||
|
|
e06d6ce4a2 | ||
|
|
58a0c6f9d3 | ||
|
|
f310f1e96c | ||
|
|
c7bf1aa222 | ||
|
|
be3fff9282 | ||
|
|
e52cf09bc1 | ||
|
|
57de1388e8 | ||
|
|
7ee0ce745e | ||
|
|
469743cd23 | ||
|
|
91e1d59869 | ||
|
|
5fb97f05a3 | ||
|
|
618b42980d | ||
|
|
211b9ea8b9 | ||
|
|
833abc27cc | ||
|
|
3d5a3f03fa | ||
|
|
51de26960c | ||
|
|
b67526aae8 | ||
|
|
14bf057c62 | ||
|
|
8eb53ea7ca | ||
|
|
9b60af4d52 | ||
|
|
956f99365a | ||
|
|
9904415144 | ||
|
|
83febbdc7d | ||
|
|
6b026e399c | ||
|
|
a661c03743 | ||
|
|
ffc03735e8 | ||
|
|
c7e24488eb | ||
|
|
fe7d2c57e3 | ||
|
|
4fc80b5057 | ||
|
|
12f3f5dcbb | ||
|
|
16f2ffc29d | ||
|
|
86a978d9ee | ||
|
|
f6fe3df59b | ||
|
|
e0764bb25e | ||
|
|
1ba0bfd10c | ||
|
|
593ab75a76 | ||
|
|
3589554028 | ||
|
|
1a59346b51 | ||
|
|
ff124465fd | ||
|
|
de5223db66 | ||
|
|
cc1395797e | ||
|
|
122791646e | ||
|
|
a3c4de52d6 | ||
|
|
95de6cf5a7 | ||
|
|
d788c84440 | ||
|
|
96046ffd30 | ||
|
|
b5e92a831b | ||
|
|
a2788129ff | ||
|
|
5bbf220077 | ||
|
|
f5d4e3f94c | ||
|
|
04ea680e9a | ||
|
|
3a940781d9 | ||
|
|
00fcd63b88 | ||
|
|
f3093649de | ||
|
|
670e33ab27 | ||
|
|
3606664cf7 | ||
|
|
81f45208b0 | ||
|
|
9145d05397 | ||
|
|
ef566836c4 | ||
|
|
10dfca1a75 | ||
|
|
b3e3f041fe | ||
|
|
e3ec361552 | ||
|
|
bfccc7133d | ||
|
|
f41a699f5d | ||
|
|
7d029778e6 | ||
|
|
26857fabdc | ||
|
|
a9b119833a | ||
|
|
c5ffe8a974 | ||
|
|
df1b4e1f21 | ||
|
|
61686aff34 | ||
|
|
ad6645c48d | ||
|
|
1e328c3546 | ||
|
|
0e2a625815 | ||
|
|
6eb66770a4 | ||
|
|
84729c4dfa | ||
|
|
8de8877c7c | ||
|
|
fdc6c3722a | ||
|
|
deca475972 | ||
|
|
ba070e21e4 | ||
|
|
35e9fd1bb0 | ||
|
|
4906609dd9 | ||
|
|
682353f9cc | ||
|
|
25c6671a87 | ||
|
|
c67702a498 | ||
|
|
5880cd88c8 | ||
|
|
54ceb4211e | ||
|
|
7a46227141 | ||
|
|
b904aa78e8 | ||
|
|
8185bee4b8 | ||
|
|
2e1b3325c6 | ||
|
|
1b8f1650dc | ||
|
|
1070b34996 | ||
|
|
13b1b73c15 | ||
|
|
e1de72d0ac | ||
|
|
dc837eb8a3 | ||
|
|
f6b76e6ecb | ||
|
|
e7cbcc83a4 | ||
|
|
390700ca71 | ||
|
|
b604321169 | ||
|
|
62c49f13ce | ||
|
|
eba75dbc2e | ||
|
|
32d2d0f0d0 | ||
|
|
81e0342bf6 | ||
|
|
56599621cc | ||
|
|
261bf12c91 | ||
|
|
b08fb86637 | ||
|
|
b290fd33b9 | ||
|
|
966bbd476f | ||
|
|
cf9bfe9e66 | ||
|
|
bb4a05032b | ||
|
|
7039e01136 | ||
|
|
e9f43f1097 | ||
|
|
150789690f | ||
|
|
e284233013 | ||
|
|
d06d6c6964 | ||
|
|
977a4477f6 | ||
|
|
33c6410809 | ||
|
|
7e321afd2a | ||
|
|
548ea1e54a | ||
|
|
4755c5f8c8 | ||
|
|
ea1bfd715e | ||
|
|
b1281c3fb9 | ||
|
|
257e9b04fe | ||
|
|
8643870822 | ||
|
|
6819696222 | ||
|
|
193bbd4e00 | ||
|
|
1bf168348e | ||
|
|
f8b4c1b389 | ||
|
|
830fba64b1 | ||
|
|
1cd61952b0 | ||
|
|
4ee1cc3b37 | ||
|
|
11dd904b6d | ||
|
|
0c03b6ddc7 | ||
|
|
176a3913aa | ||
|
|
6f0b00f983 | ||
|
|
69ace201f8 | ||
|
|
a04f31c5c0 | ||
|
|
1d9f47d1e5 | ||
|
|
36a91fc6ff | ||
|
|
cd3ed71137 | ||
|
|
73e56527bf | ||
|
|
24059924b1 | ||
|
|
bb100a2511 | ||
|
|
0c8c32cd03 | ||
|
|
a38d6e74f5 | ||
|
|
05d8dacb27 | ||
|
|
d6a0859eab | ||
|
|
4912967eab | ||
|
|
76e1cda495 | ||
|
|
c1a23bf450 | ||
|
|
b25b5bf5f6 | ||
|
|
079f17761c | ||
|
|
e1b8c425da | ||
|
|
5f05756e65 | ||
|
|
c0e996acf8 | ||
|
|
41ec58a524 | ||
|
|
0a5e7be129 | ||
|
|
b23d65b014 | ||
|
|
60ef7903e2 | ||
|
|
ee8fd21d28 | ||
|
|
232ed91ec7 | ||
|
|
7fccad156e | ||
|
|
11e56456a0 | ||
|
|
d07e78c59a | ||
|
|
c45479a6e5 | ||
|
|
345a528625 | ||
|
|
01d8490255 | ||
|
|
ea4b37d5c5 | ||
|
|
a1319cb8aa | ||
|
|
35c49d62d7 | ||
|
|
0576a11a6f | ||
|
|
c8f86d94c9 | ||
|
|
2b24eab26a | ||
|
|
7bbc7a61ce | ||
|
|
fe26284376 | ||
|
|
ab62fe6496 | ||
|
|
bc9bae0f7f | ||
|
|
e5939d1a18 | ||
|
|
5fadb7f200 | ||
|
|
a02aa7a316 | ||
|
|
3ab26a5d40 | ||
|
|
a8bae31939 | ||
|
|
faea588fb8 | ||
|
|
792e246262 | ||
|
|
85f19f9b8c | ||
|
|
40e4f49dbe | ||
|
|
05c721bebb | ||
|
|
e995cc9329 | ||
|
|
9457c115be | ||
|
|
1eddc79a90 | ||
|
|
ccfc909eb2 | ||
|
|
1a93cbba1e | ||
|
|
529d410bec | ||
|
|
4d1c0eaa52 | ||
|
|
adfd3c1098 | ||
|
|
f7d0c4b065 | ||
|
|
5fb32f1e2d | ||
|
|
f69489a4fc | ||
|
|
d34ce26c4e | ||
|
|
3f7fe94009 | ||
|
|
ae593decfc | ||
|
|
eea62125a1 | ||
|
|
96f36a63dc | ||
|
|
19e183f02f | ||
|
|
57f3df3cab | ||
|
|
08e78e63cf | ||
|
|
9228dffe5e | ||
|
|
93dc7d4cc1 | ||
|
|
1bead8adf7 | ||
|
|
6681f3bfec | ||
|
|
71f8960ef1 | ||
|
|
01780f19b1 | ||
|
|
1fa0c4d4d3 | ||
|
|
eebe126842 | ||
|
|
ebfa285122 | ||
|
|
27212719dc | ||
|
|
17567028da | ||
|
|
34fd8e0e00 | ||
|
|
6cf42075fc | ||
|
|
afd8d2f9ba | ||
|
|
1d54bff385 | ||
|
|
6dd0013a5d | ||
|
|
f2b5292bcb | ||
|
|
3a94f6e8b3 | ||
|
|
f2846a0b78 | ||
|
|
dbde7033d8 | ||
|
|
18f04adccb | ||
|
|
1ed65b6bd7 | ||
|
|
9b781c4c06 | ||
|
|
25f9105a97 | ||
|
|
602109bd8d | ||
|
|
631d27f7a8 | ||
|
|
aad27a7a68 | ||
|
|
4ac01154d9 | ||
|
|
4e2c7c57d7 | ||
|
|
6e65cfcc9a | ||
|
|
96a4b7eaa7 | ||
|
|
64afada7f0 | ||
|
|
9f563f4873 | ||
|
|
ca61d0ee8b | ||
|
|
e96dabadf1 | ||
|
|
b6601338c2 | ||
|
|
8167e1e07e | ||
|
|
23ce927301 | ||
|
|
c7941fc7b0 | ||
|
|
1078ad9ae9 | ||
|
|
c0085cbc61 | ||
|
|
a49d245b92 | ||
|
|
0ce6829e4c | ||
|
|
4e3acdcbdc | ||
|
|
85f808130d |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -4,6 +4,8 @@
|
||||
Doxyfile.help
|
||||
Makefile
|
||||
autom4te.cache/
|
||||
builtin_scripts.cpp
|
||||
builtin_scripts.h
|
||||
command_list.txt
|
||||
config.h
|
||||
config.h.in
|
||||
@@ -13,7 +15,6 @@ configure
|
||||
doc.h
|
||||
doc_src/commands.hdr
|
||||
doc_src/index.hdr
|
||||
etc/config.fish
|
||||
po/*.gmo
|
||||
fish
|
||||
fish.spec
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
5
INSTALL
5
INSTALL
@@ -53,6 +53,11 @@ following command:
|
||||
|
||||
chsh will prompt you for your password, and change your default shell.
|
||||
|
||||
To switch your default shell back, you can run:
|
||||
|
||||
% chsh -s /bin/bash
|
||||
|
||||
Substitute /bin/bash with /bin/tcsh or /bin/zsh as appropriate.
|
||||
|
||||
Local install procedure
|
||||
=======================
|
||||
|
||||
108
Makefile.in
108
Makefile.in
@@ -55,7 +55,6 @@ mandir = @mandir@
|
||||
sysconfdir = @sysconfdir@
|
||||
docdir = @docdir@
|
||||
localedir = @localedir@
|
||||
prefix = @prefix@
|
||||
optbindirs = @optbindirs@
|
||||
|
||||
#
|
||||
@@ -225,20 +224,6 @@ MAIN_DIR_FILES_UNSORTED := Doxyfile Doxyfile.user Doxyfile.help.in \
|
||||
MAIN_DIR_FILES := $(sort $(MAIN_DIR_FILES_UNSORTED))
|
||||
|
||||
|
||||
#
|
||||
# Files in ./etc/
|
||||
#
|
||||
|
||||
ETC_DIR_FILES :=etc/config.fish.in
|
||||
|
||||
|
||||
#
|
||||
# Files in ./share/
|
||||
#
|
||||
|
||||
SHARE_DIR_FILES :=share/config.fish.in
|
||||
|
||||
|
||||
#
|
||||
# Files in ./tests/
|
||||
#
|
||||
@@ -296,7 +281,7 @@ XSEL_BIN := @XSEL_BIN@
|
||||
# Make everything needed for installing fish
|
||||
#
|
||||
|
||||
all: $(PROGRAMS) user_doc share/man etc/config.fish share/config.fish $(TRANSLATIONS)
|
||||
all: $(PROGRAMS) user_doc share/man $(TRANSLATIONS)
|
||||
@echo fish has now been built.
|
||||
@echo Use \'$(MAKE) install\' to install fish.
|
||||
.PHONY: all
|
||||
@@ -319,12 +304,9 @@ Makefile: Makefile.in configure
|
||||
# and should only be used when debuging fish.
|
||||
#
|
||||
|
||||
debug:
|
||||
$(MAKE) fish EXTRA_CXXFLAGS="-O0 -Wno-unused -g"
|
||||
.PHONY: debug
|
||||
|
||||
prof:
|
||||
$(MAKE) all EXTRA_CXXFLAGS="-pg" LDFLAGS="-pg"
|
||||
prof: EXTRA_CXXFLAGS += -pg
|
||||
prof: LDFLAGS += -pg
|
||||
prof: all
|
||||
.PHONY: prof
|
||||
|
||||
#
|
||||
@@ -335,10 +317,8 @@ prof:
|
||||
# intermediate *.hdr and doc.h files if needed
|
||||
# Allow doxygen to fail, e.g. if it does not exist
|
||||
|
||||
user_doc: $(HDR_FILES_SRC) Doxyfile.user user_doc.head.html $(HELP_SRC)
|
||||
$(MAKE) doc.h $(HDR_FILES)
|
||||
- doxygen Doxyfile.user
|
||||
touch user_doc
|
||||
user_doc: $(HDR_FILES_SRC) Doxyfile.user user_doc.head.html $(HELP_SRC) doc.h $(HDR_FILES)
|
||||
- doxygen Doxyfile.user && touch user_doc
|
||||
|
||||
|
||||
#
|
||||
@@ -472,10 +452,10 @@ doc.h: $(HDR_FILES)
|
||||
# Create a template translation object
|
||||
#
|
||||
|
||||
messages.pot: *.cpp *.h etc/*.in share/*.in share/completions/*.fish share/functions/*.fish seq
|
||||
messages.pot: *.cpp *.h share/completions/*.fish share/functions/*.fish seq
|
||||
if test $(HAVE_GETTEXT) = 1;then \
|
||||
xgettext -k_ -kN_ *.cpp *.h -o messages.pot; \
|
||||
if xgettext -j -k_ -kN_ -k--description -LShell etc/*.in share/*.in share/completions/*.fish share/functions/*.fish seq -o messages.pot; then true; else \
|
||||
if xgettext -j -k_ -kN_ -k--description -LShell share/completions/*.fish share/functions/*.fish seq -o messages.pot; then true; else \
|
||||
echo "Your xgettext version is too old to build the messages.pot file"\
|
||||
rm messages.pot\
|
||||
false;\
|
||||
@@ -606,9 +586,10 @@ install-force: all install-translations
|
||||
$(INSTALL) -m 755 -d $(DESTDIR)$(datadir)/fish
|
||||
$(INSTALL) -m 755 -d $(DESTDIR)$(datadir)/fish/completions
|
||||
$(INSTALL) -m 755 -d $(DESTDIR)$(datadir)/fish/functions
|
||||
$(INSTALL) -m 755 -d $(DESTDIR)$(datadir)/fish/man
|
||||
$(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/sample_prompts
|
||||
$(INSTALL) -m 644 etc/config.fish $(DESTDIR)$(sysconfdir)/fish/
|
||||
$(INSTALL) -m 644 share/config.fish $(DESTDIR)$(datadir)/fish/
|
||||
for i in $(COMPLETIONS_DIR_FILES); do \
|
||||
@@ -620,7 +601,7 @@ install-force: all install-translations
|
||||
true; \
|
||||
done;
|
||||
for i in share/man/*.1; do \
|
||||
$(INSTALL) -m 644 $$i $(DESTDIR)$(datadir)/fish/man/; \
|
||||
$(INSTALL) -m 644 $$i $(DESTDIR)$(datadir)/fish/man/man1/; \
|
||||
true; \
|
||||
done;
|
||||
for i in share/tools/*.py; do\
|
||||
@@ -631,6 +612,10 @@ install-force: all install-translations
|
||||
$(INSTALL) -m 644 $$i $(DESTDIR)$(datadir)/fish/tools/web_config/; \
|
||||
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; \
|
||||
done;
|
||||
for i in share/tools/web_config/*.py; do\
|
||||
$(INSTALL) -m 755 $$i $(DESTDIR)$(datadir)/fish/tools/web_config/; \
|
||||
true; \
|
||||
@@ -656,6 +641,7 @@ install-force: all install-translations
|
||||
@echo
|
||||
@echo To set your colors, run \'fish_config\'
|
||||
@echo To scan your man pages for completions, run \'fish_update_completions\'
|
||||
@echo To autocomplete command suggestions press Ctrl + F or right arrow key.
|
||||
@echo
|
||||
@echo Have fun!
|
||||
.PHONY: install-force
|
||||
@@ -670,16 +656,15 @@ uninstall: uninstall-translations
|
||||
rm -f $(DESTDIR)$(bindir)/$$i; \
|
||||
done;
|
||||
-rm -f $(DESTDIR)$(bindir)/xsel
|
||||
-rm -f $(DESTDIR)$(sysconfdir)/fish/config.fish
|
||||
-rmdir $(DESTDIR)$(sysconfdir)/fish
|
||||
-rm -rf $(DESTDIR)$(sysconfdir)/fish
|
||||
-if test -d $(DESTDIR)$(datadir)/fish; then \
|
||||
rm -r $(DESTDIR)$(datadir)/fish; \
|
||||
fi
|
||||
-if test -d $(DESTDIR)$(docdir); then \
|
||||
rm -r $(DESTDIR)$(docdir);\
|
||||
rm -rf $(DESTDIR)$(docdir);\
|
||||
fi
|
||||
-for i in $(MANUALS); do \
|
||||
rm -f $(DESTDIR)$(mandir)/man1/`basename $$i`*; \
|
||||
rm -rf $(DESTDIR)$(mandir)/man1/`basename $$i`*; \
|
||||
done;
|
||||
.PHONY: uninstall
|
||||
|
||||
@@ -821,49 +806,6 @@ depend:
|
||||
#
|
||||
autoload.cpp function.cpp: $(GENERATED_INTERN_SCRIPT_FILES)
|
||||
|
||||
#
|
||||
# Copy all the source files into a new directory and use tar to create
|
||||
# an archive from it. Simplest way I could think of to make an archive
|
||||
# witout backups, autogenerated files, etc.
|
||||
#
|
||||
# Uses install instead of mkdir so build won't fail if the directory
|
||||
# exists
|
||||
#
|
||||
|
||||
fish-@PACKAGE_VERSION@.tar: $(DOC_SRC_DIR_FILES) $(MAIN_DIR_FILES) $(ETC_DIR_FILES) $(TEST_DIR_FILES) $(SHARE_DIR_FILES) $(FUNCTIONS_DIR_FILES) $(COMPLETIONS_DIR_FILES) ChangeLog user_doc share/man
|
||||
rm -rf fish-@PACKAGE_VERSION@
|
||||
$(INSTALL) -d fish-@PACKAGE_VERSION@
|
||||
$(INSTALL) -d fish-@PACKAGE_VERSION@/doc_src
|
||||
$(INSTALL) -d fish-@PACKAGE_VERSION@/user_doc
|
||||
$(INSTALL) -d fish-@PACKAGE_VERSION@/etc
|
||||
$(INSTALL) -d fish-@PACKAGE_VERSION@/share
|
||||
$(INSTALL) -d fish-@PACKAGE_VERSION@/share/completions
|
||||
$(INSTALL) -d fish-@PACKAGE_VERSION@/share/functions
|
||||
$(INSTALL) -d fish-@PACKAGE_VERSION@/share/man
|
||||
$(INSTALL) -d fish-@PACKAGE_VERSION@/tests
|
||||
$(INSTALL) -d fish-@PACKAGE_VERSION@/po
|
||||
cp -f $(DOC_SRC_DIR_FILES) fish-@PACKAGE_VERSION@/doc_src
|
||||
cp -f $(MAIN_DIR_FILES) fish-@PACKAGE_VERSION@/
|
||||
cp -f $(ETC_DIR_FILES) fish-@PACKAGE_VERSION@/etc/
|
||||
cp -f $(SHARE_DIR_FILES) fish-@PACKAGE_VERSION@/share/
|
||||
cp -f $(COMPLETIONS_DIR_FILES) fish-@PACKAGE_VERSION@/share/completions/
|
||||
cp -f $(FUNCTIONS_DIR_FILES) fish-@PACKAGE_VERSION@/share/functions/
|
||||
cp -f $(TESTS_DIR_FILES) fish-@PACKAGE_VERSION@/tests/
|
||||
cp -f $(TRANSLATIONS_SRC) fish-@PACKAGE_VERSION@/po/
|
||||
cp -f share/man/*.1 fish-@PACKAGE_VERSION@/share/man/
|
||||
cp -rf user_doc fish-@PACKAGE_VERSION@/
|
||||
tar -c fish-@PACKAGE_VERSION@ >fish-@PACKAGE_VERSION@.tar
|
||||
rm -rf fish-@PACKAGE_VERSION@
|
||||
|
||||
|
||||
#
|
||||
# Just an alias for fish-@PACKAGE_VERSION@.tar
|
||||
#
|
||||
|
||||
tar: fish-@PACKAGE_VERSION@.tar
|
||||
.PHONY: tar
|
||||
|
||||
|
||||
#
|
||||
# Make compressed tar archives
|
||||
#
|
||||
@@ -916,7 +858,6 @@ rpm: fish-@PACKAGE_VERSION@.tar.bz2 fish.spec
|
||||
|
||||
distclean: clean
|
||||
rm -f fish.spec Doxyfile.help
|
||||
rm -f etc/config.fish seq share/config.fish
|
||||
rm -f config.status config.log config.h Makefile
|
||||
rm -rf $(XSEL)
|
||||
.PHONY: distclean
|
||||
@@ -927,16 +868,23 @@ distclean: clean
|
||||
# are created by the configure script.
|
||||
#
|
||||
|
||||
# Don't delete the docs unless we have Doxygen installed
|
||||
# We provide pre-built docs in the tarball, and if they get
|
||||
# deleted we won't be able to regenerate them
|
||||
|
||||
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 tokenizer_test key_reader
|
||||
rm -f share/config.fish etc/config.fish doc_src/index.hdr doc_src/commands.hdr
|
||||
rm -f command_list.txt toc.txt
|
||||
rm -f doc_src/index.hdr doc_src/commands.hdr
|
||||
rm -f fish-@PACKAGE_VERSION@.tar
|
||||
rm -f fish-@PACKAGE_VERSION@.tar.gz
|
||||
rm -f fish-@PACKAGE_VERSION@.tar.bz2
|
||||
rm -rf doc;
|
||||
if command -v doxygen; then \
|
||||
rm -rf doc user_doc share/man; \
|
||||
fi
|
||||
rm -rf fish-@PACKAGE_VERSION@
|
||||
rm -f $(TRANSLATIONS)
|
||||
test ! -d "$(XSEL)" || make -C $(XSEL) clean
|
||||
|
||||
15
README
15
README
@@ -1,15 +0,0 @@
|
||||
How to find documentation for fish
|
||||
==================================
|
||||
|
||||
The fish documentation is distributed in an intermediate format. To
|
||||
view it, you have to type:
|
||||
|
||||
% make user_doc
|
||||
|
||||
Which will create the directory user_doc, containing html
|
||||
documentation for fish. If you build and install fish, the
|
||||
documentation will be available through the 'help' builtin.
|
||||
|
||||
After installation, you can start fish by typing fish in the
|
||||
terminal. After fish has started, try using the help command for more
|
||||
information.
|
||||
39
README.md
Normal file
39
README.md
Normal file
@@ -0,0 +1,39 @@
|
||||
[fish](http://ridiculousfish.com/shell/) - the friendly interactive shell
|
||||
================================================
|
||||
|
||||
fish is a smart and user-friendly command line shell for OS X, Linux, and the rest of the family. fish includes features like syntax highlighting, autosuggest-as-you-type, and fancy tab completions that just work, with no configuration required.
|
||||
|
||||
For more on fish's design philosophy, see the [design document](http://ridiculousfish.com/shell/user_doc/html/design.html).
|
||||
|
||||
## Quick Start
|
||||
|
||||
fish generally works like other shells, like bash or zsh. A few important differences are documented at <http://ridiculousfish.com/shell/faq.html>
|
||||
|
||||
Detailed user documentation is available by running `help` within fish, and also at <http://ridiculousfish.com/shell/user_doc/html/>
|
||||
|
||||
## Building
|
||||
|
||||
fish can be built using autotools or Xcode.
|
||||
|
||||
### Autotools Build
|
||||
|
||||
autoconf
|
||||
./configure [--without-xsel]
|
||||
make [gmake on BSD]
|
||||
sudo make install
|
||||
|
||||
### Xcode Development Build
|
||||
|
||||
* Build the `base` target in Xcode
|
||||
* Run the fish executable, for example, in `DerivedData/FishsFish/Build/Products/Debug/base/bin/fish`
|
||||
|
||||
### Xcode Build and Install
|
||||
|
||||
mkdir /tmp/fish_build
|
||||
xcodebuild install -target install_tree DSTROOT=/tmp/fish_build
|
||||
sudo ditto /tmp/fish_build/ /
|
||||
|
||||
## Contact Us
|
||||
|
||||
Questions, comments, rants and raves can be posted to the official fish mailing list at <https://lists.sourceforge.net/lists/listinfo/fish-users>
|
||||
Found a bug? Have an awesome idea? Please open an issue on this github page.
|
||||
24
autoload.cpp
24
autoload.cpp
@@ -39,9 +39,12 @@ file_access_attempt_t access_file(const wcstring &path, int mode) {
|
||||
}
|
||||
|
||||
autoload_t::autoload_t(const wcstring &env_var_name_var, const builtin_script_t * const scripts, size_t script_count) :
|
||||
lock(),
|
||||
env_var_name(env_var_name_var),
|
||||
builtin_scripts(scripts),
|
||||
builtin_script_count(script_count)
|
||||
builtin_script_count(script_count),
|
||||
last_path(),
|
||||
is_loading_set()
|
||||
{
|
||||
pthread_mutex_init(&lock, NULL);
|
||||
}
|
||||
@@ -108,19 +111,18 @@ int autoload_t::load( const wcstring &cmd, bool reload )
|
||||
res = this->locate_file_and_maybe_load_it( cmd, true, reload, path_list );
|
||||
|
||||
/* Clean up */
|
||||
int erased = is_loading_set.erase(cmd);
|
||||
bool erased = !! is_loading_set.erase(cmd);
|
||||
assert(erased);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool autoload_t::can_load( const wcstring &cmd, const env_vars &vars )
|
||||
bool autoload_t::can_load( const wcstring &cmd, const env_vars_snapshot_t &vars )
|
||||
{
|
||||
const wchar_t *path_var_ptr = vars.get(env_var_name.c_str());
|
||||
if (! path_var_ptr || ! path_var_ptr[0])
|
||||
const env_var_t path_var = vars.get(env_var_name);
|
||||
if (path_var.missing_or_empty())
|
||||
return false;
|
||||
|
||||
const wcstring path_var(path_var_ptr);
|
||||
|
||||
std::vector<wcstring> path_list;
|
||||
tokenize_variable_array( path_var, path_list );
|
||||
return this->locate_file_and_maybe_load_it( cmd, false, false, path_list );
|
||||
@@ -197,12 +199,12 @@ bool autoload_t::locate_file_and_maybe_load_it( const wcstring &cmd, bool really
|
||||
if (! func) {
|
||||
/* Can't use a function that doesn't exist */
|
||||
use_cached = false;
|
||||
} else if ( ! allow_stale_functions && is_stale(func)) {
|
||||
/* Can't use a stale function */
|
||||
use_cached = false;
|
||||
} else if (really_load && ! func->is_placeholder && ! func->is_loaded) {
|
||||
/* Can't use an unloaded function */
|
||||
use_cached = false;
|
||||
} else if ( ! allow_stale_functions && is_stale(func)) {
|
||||
/* Can't use a stale function */
|
||||
use_cached = false;
|
||||
} else {
|
||||
/* I guess we can use it */
|
||||
use_cached = true;
|
||||
@@ -266,7 +268,7 @@ bool autoload_t::locate_file_and_maybe_load_it( const wcstring &cmd, bool really
|
||||
autoload_function_t *func = this->get_node(cmd);
|
||||
|
||||
/* Generate the source if we need to load it */
|
||||
bool need_to_load_function = really_load && (func == NULL || func->access.mod_time == access.mod_time || ! func->is_loaded);
|
||||
bool need_to_load_function = really_load && (func == NULL || func->access.mod_time != access.mod_time || ! func->is_loaded);
|
||||
if (need_to_load_function) {
|
||||
|
||||
/* Generate the script source */
|
||||
|
||||
@@ -35,7 +35,7 @@ struct autoload_function_t : public lru_node_t
|
||||
|
||||
|
||||
struct builtin_script_t;
|
||||
class env_vars;
|
||||
class env_vars_snapshot_t;
|
||||
|
||||
/**
|
||||
A class that represents a path from which we can autoload, and the autoloaded contents.
|
||||
@@ -122,7 +122,7 @@ class autoload_t : private lru_cache_t<autoload_function_t> {
|
||||
void unload_all( );
|
||||
|
||||
/** Check whether the given command could be loaded, but do not load it. */
|
||||
bool can_load( const wcstring &cmd, const env_vars &vars );
|
||||
bool can_load( const wcstring &cmd, const env_vars_snapshot_t &vars );
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -1,9 +1,46 @@
|
||||
#!/bin/sh
|
||||
|
||||
rm -f ~/fish_built/fishfish.tar.gz
|
||||
if git archive --format=tar --prefix=fishfish/ fish_fish | gzip - > ~/fish_built/fishfish.tar.gz
|
||||
then
|
||||
echo "Tarball written to ~/fish_built/fishfish.tar.gz"
|
||||
else
|
||||
echo "Tarball could not be written"
|
||||
fi
|
||||
# Script to generate a tarball
|
||||
# We use git to output a tree. But we also want to build the user documentation
|
||||
# and put that in the tarball, so that nobody needs to have doxygen installed
|
||||
# to build it.
|
||||
|
||||
# Exit on error
|
||||
set -e
|
||||
|
||||
# We wil generate a tarball with a prefix "fish"
|
||||
# git can do that automatically for us via git-archive
|
||||
# but to get the documentation in, we need to make a symlink called "fish"
|
||||
# and tar from that, so that the documentation gets the right prefix
|
||||
|
||||
# Get the current directory, which we'll use for symlinks
|
||||
wd="$PWD"
|
||||
|
||||
# The name of the prefix, which is the directory that you get when you untar
|
||||
prefix="fish"
|
||||
|
||||
# The path where we will output the tar file
|
||||
path=~/fish_built/fish-2.0.tar
|
||||
|
||||
# Clean up stuff we've written before
|
||||
rm -f "$path" "$path".gz
|
||||
|
||||
# git starts the archive
|
||||
git archive --format=tar --prefix="$prefix"/ master > "$path"
|
||||
|
||||
# tarball out the documentation
|
||||
make user_doc
|
||||
make share/man
|
||||
cd /tmp
|
||||
rm -f "$prefix"
|
||||
ln -s "$wd" "$prefix"
|
||||
tar --append --file="$path" "$prefix"/user_doc/html
|
||||
tar --append --file="$path" "$prefix"/share/man
|
||||
rm -f "$prefix"
|
||||
|
||||
# gzip it
|
||||
gzip "$path"
|
||||
|
||||
# Output what we did, and the sha1 hash
|
||||
echo "Tarball written to $path".gz
|
||||
openssl sha1 "$path".gz
|
||||
|
||||
@@ -21,7 +21,7 @@ tmpfile=${file}.tmp
|
||||
set -o noclobber
|
||||
|
||||
trap "rm -f $tmpfile" EXIT
|
||||
|
||||
|
||||
if ! cat $file > $tmpfile
|
||||
then
|
||||
cat 1>&2 <<EOF
|
||||
@@ -31,11 +31,16 @@ EOF
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Append a newline if it doesn't exist
|
||||
if [ "$(tail -c1 "$tmpfile"; echo x)" != $'\nx' ]; then
|
||||
echo "" >> "$tmpfile"
|
||||
fi
|
||||
|
||||
for i
|
||||
do
|
||||
if ! grep -q "^${i}$" $tmpfile
|
||||
if ! grep -q "^${i}$" "$tmpfile"
|
||||
then
|
||||
echo $i >> $tmpfile
|
||||
echo $i >> "$tmpfile"
|
||||
fi
|
||||
done
|
||||
|
||||
|
||||
526
builtin.cpp
526
builtin.cpp
File diff suppressed because it is too large
Load Diff
@@ -73,6 +73,10 @@ enum
|
||||
|
||||
#define BUILTIN_FOR_ERR_NAME _( L"%ls: '%ls' is not a valid variable name\n" )
|
||||
|
||||
/** Error messages for 'else if' */
|
||||
#define BUILTIN_ELSEIF_ERR_COUNT _( L"%ls: can only take 'if' and then another command as an argument\n")
|
||||
#define BUILTIN_ELSEIF_ERR_ARGUMENT _( L"%ls: any second argument must be 'if'\n")
|
||||
|
||||
/**
|
||||
Error message when too many arguments are supplied to a builtin
|
||||
*/
|
||||
@@ -135,7 +139,7 @@ int builtin_exists( const wcstring &cmd );
|
||||
|
||||
\return the exit status of the builtin command
|
||||
*/
|
||||
int builtin_run( parser_t &parser, const wchar_t * const *argv, io_data_t *io );
|
||||
int builtin_run( parser_t &parser, const wchar_t * const *argv, const io_chain_t &io );
|
||||
|
||||
/** Returns a list of all builtin names */
|
||||
wcstring_list_t builtin_get_names(void);
|
||||
|
||||
@@ -61,7 +61,7 @@ static const wchar_t *current_buffer=0;
|
||||
What the commandline builtin considers to be the current cursor
|
||||
position.
|
||||
*/
|
||||
static int current_cursor_pos = -1;
|
||||
static size_t current_cursor_pos = (size_t)(-1);
|
||||
|
||||
/**
|
||||
Returns the current commandline buffer.
|
||||
@@ -74,7 +74,7 @@ static const wchar_t *get_buffer()
|
||||
/**
|
||||
Returns the position of the cursor
|
||||
*/
|
||||
static int get_cursor_pos()
|
||||
static size_t get_cursor_pos()
|
||||
{
|
||||
return current_cursor_pos;
|
||||
}
|
||||
@@ -94,7 +94,7 @@ static void replace_part( const wchar_t *begin,
|
||||
int append_mode )
|
||||
{
|
||||
const wchar_t *buff = get_buffer();
|
||||
int out_pos=get_cursor_pos();
|
||||
size_t out_pos = get_cursor_pos();
|
||||
|
||||
wcstring out;
|
||||
|
||||
@@ -118,7 +118,7 @@ static void replace_part( const wchar_t *begin,
|
||||
}
|
||||
case INSERT_MODE:
|
||||
{
|
||||
int cursor = get_cursor_pos() -(begin-buff);
|
||||
long cursor = get_cursor_pos() -(begin-buff);
|
||||
out.append( begin, cursor );
|
||||
out.append( insert );
|
||||
out.append( begin+cursor, end-begin-cursor );
|
||||
@@ -531,7 +531,7 @@ static int builtin_commandline( parser_t &parser, wchar_t **argv )
|
||||
if( argc-woptind )
|
||||
{
|
||||
wchar_t *endptr;
|
||||
int new_pos;
|
||||
long new_pos;
|
||||
errno = 0;
|
||||
|
||||
new_pos = wcstol( argv[woptind], &endptr, 10 );
|
||||
@@ -545,13 +545,13 @@ static int builtin_commandline( parser_t &parser, wchar_t **argv )
|
||||
}
|
||||
|
||||
current_buffer = reader_get_buffer();
|
||||
new_pos = maxi( 0, mini( new_pos, wcslen( current_buffer ) ) );
|
||||
reader_set_buffer( current_buffer, new_pos );
|
||||
new_pos = maxi( 0L, mini( new_pos, (long)wcslen( current_buffer ) ) );
|
||||
reader_set_buffer( current_buffer, (size_t)new_pos );
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
append_format(stdout_buffer, L"%d\n", reader_get_cursor_pos() );
|
||||
append_format(stdout_buffer, L"%lu\n", (unsigned long)reader_get_cursor_pos() );
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -559,9 +559,9 @@ static int builtin_commandline( parser_t &parser, wchar_t **argv )
|
||||
|
||||
if( line_mode )
|
||||
{
|
||||
int pos = reader_get_cursor_pos();
|
||||
size_t pos = reader_get_cursor_pos();
|
||||
const wchar_t *buff = reader_get_buffer();
|
||||
append_format(stdout_buffer, L"%d\n", parse_util_lineno( buff, pos ) );
|
||||
append_format(stdout_buffer, L"%lu\n", (unsigned long)parse_util_lineno( buff, pos ) );
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ static void builtin_jobs_print( const job_t *j, int mode, int header )
|
||||
stdout_buffer.append(job_is_stopped(j)?_(L"stopped"):_(L"running"));
|
||||
stdout_buffer.append(L"\t");
|
||||
stdout_buffer.append(j->command_wcstr());
|
||||
stdout_buffer.append(L"\t");
|
||||
stdout_buffer.append(L"\n");
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -292,10 +292,10 @@ static int builtin_jobs( parser_t &parser, wchar_t **argv )
|
||||
|
||||
for( i=woptind; i<argc; i++ )
|
||||
{
|
||||
long pid;
|
||||
int pid;
|
||||
wchar_t *end;
|
||||
errno=0;
|
||||
pid=wcstol( argv[i], &end, 10 );
|
||||
pid=fish_wcstoi( argv[i], &end, 10 );
|
||||
if( errno || *end )
|
||||
{
|
||||
append_format(stderr_buffer,
|
||||
|
||||
230
builtin_set.cpp
230
builtin_set.cpp
@@ -32,7 +32,7 @@ extern wcstring stdout_buffer, stderr_buffer;
|
||||
/**
|
||||
Error message for invalid path operations
|
||||
*/
|
||||
#define BUILTIN_SET_PATH_ERROR L"%ls: Could not add component %ls to %ls.\n"
|
||||
#define BUILTIN_SET_PATH_ERROR L"%ls: Warning: path component %ls may not be valid in %ls.\n"
|
||||
|
||||
/**
|
||||
Hint for invalid path operation with a colon
|
||||
@@ -56,105 +56,107 @@ static int is_path_variable( const wchar_t *env )
|
||||
Call env_set. If this is a path variable, e.g. PATH, validate the
|
||||
elements. On error, print a description of the problem to stderr.
|
||||
*/
|
||||
static int my_env_set( const wchar_t *key, wcstring_list_t &val, int scope )
|
||||
static int my_env_set( const wchar_t *key, const wcstring_list_t &val, int scope )
|
||||
{
|
||||
size_t i;
|
||||
int retcode = 0;
|
||||
const wchar_t *val_str=NULL;
|
||||
|
||||
if( is_path_variable( key ) )
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
for( i=0; i< val.size() ; i++ )
|
||||
{
|
||||
int show_perror = 0;
|
||||
int show_hint = 0;
|
||||
|
||||
struct stat buff;
|
||||
const wchar_t *dir = val[i].c_str();
|
||||
|
||||
if( wstat( dir, &buff ) )
|
||||
{
|
||||
error = 1;
|
||||
show_perror = 1;
|
||||
}
|
||||
|
||||
if( !( S_ISDIR(buff.st_mode) ) )
|
||||
{
|
||||
error = 1;
|
||||
|
||||
}
|
||||
|
||||
if( error )
|
||||
{
|
||||
const wchar_t *colon;
|
||||
size_t i;
|
||||
int retcode = 0;
|
||||
const wchar_t *val_str=NULL;
|
||||
|
||||
if( is_path_variable( key ) )
|
||||
{
|
||||
/* Fix for https://github.com/fish-shell/fish-shell/issues/199 . Return success if any path setting succeeds. */
|
||||
bool any_success = false, any_error = false;
|
||||
|
||||
for( i=0; i< val.size() ; i++ )
|
||||
{
|
||||
bool show_perror = false;
|
||||
int show_hint = 0;
|
||||
bool error = false;
|
||||
|
||||
struct stat buff;
|
||||
const wchar_t *dir = val[i].c_str();
|
||||
|
||||
if( wstat( dir, &buff ) )
|
||||
{
|
||||
error = true;
|
||||
show_perror = true;
|
||||
}
|
||||
|
||||
if( !( S_ISDIR(buff.st_mode) ) )
|
||||
{
|
||||
error = true;
|
||||
}
|
||||
|
||||
if( !error )
|
||||
{
|
||||
any_success = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
any_error = true;
|
||||
const wchar_t *colon;
|
||||
append_format(stderr_buffer, _(BUILTIN_SET_PATH_ERROR), L"set", dir, key);
|
||||
colon = wcschr( dir, L':' );
|
||||
|
||||
if( colon && *(colon+1) )
|
||||
{
|
||||
show_hint = 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if( show_perror )
|
||||
{
|
||||
builtin_wperror( L"set" );
|
||||
}
|
||||
|
||||
if( show_hint )
|
||||
{
|
||||
colon = wcschr( dir, L':' );
|
||||
|
||||
if( colon && *(colon+1) )
|
||||
{
|
||||
show_hint = 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if( show_perror )
|
||||
{
|
||||
builtin_wperror( L"set" );
|
||||
}
|
||||
|
||||
if( show_hint )
|
||||
{
|
||||
append_format(stderr_buffer, _(BUILTIN_SET_PATH_HINT), L"set", key, key, wcschr( dir, L':' )+1);
|
||||
}
|
||||
|
||||
if( error )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if( error )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
wcstring sb;
|
||||
if( val.size() )
|
||||
{
|
||||
for( i=0; i< val.size() ; i++ )
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Fail at setting the path if we tried to set it to something non-empty, but it wound up empty. */
|
||||
if( ! val.empty() && ! any_success )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
wcstring sb;
|
||||
if( val.size() )
|
||||
{
|
||||
for( i=0; i< val.size() ; i++ )
|
||||
{
|
||||
sb.append(val[i]);
|
||||
if( i<val.size() - 1 )
|
||||
{
|
||||
sb.append( ARRAY_SEP_STR );
|
||||
}
|
||||
}
|
||||
val_str = sb.c_str();
|
||||
}
|
||||
|
||||
switch( env_set( key, val_str, scope | ENV_USER ) )
|
||||
{
|
||||
case ENV_PERM:
|
||||
{
|
||||
if( i<val.size() - 1 )
|
||||
{
|
||||
sb.append( ARRAY_SEP_STR );
|
||||
}
|
||||
}
|
||||
val_str = sb.c_str();
|
||||
}
|
||||
|
||||
switch( env_set( key, val_str, scope | ENV_USER ) )
|
||||
{
|
||||
case ENV_PERM:
|
||||
{
|
||||
append_format(stderr_buffer, _(L"%ls: Tried to change the read-only variable '%ls'\n"), L"set", key);
|
||||
retcode=1;
|
||||
break;
|
||||
}
|
||||
|
||||
case ENV_INVALID:
|
||||
{
|
||||
append_format(stderr_buffer, _(L"%ls: Unknown error"), L"set" );
|
||||
retcode=1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return retcode;
|
||||
retcode=1;
|
||||
break;
|
||||
}
|
||||
|
||||
case ENV_INVALID:
|
||||
{
|
||||
append_format(stderr_buffer, _(L"%ls: Unknown error"), L"set" );
|
||||
retcode=1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return retcode;
|
||||
}
|
||||
|
||||
|
||||
@@ -172,7 +174,7 @@ static int my_env_set( const wchar_t *key, wcstring_list_t &val, int scope )
|
||||
static int parse_index( std::vector<long> &indexes,
|
||||
const wchar_t *src,
|
||||
const wchar_t *name,
|
||||
int var_count )
|
||||
size_t var_count )
|
||||
{
|
||||
size_t len;
|
||||
|
||||
@@ -236,9 +238,31 @@ static int parse_index( std::vector<long> &indexes,
|
||||
l_ind = var_count+l_ind+1;
|
||||
}
|
||||
|
||||
indexes.push_back( l_ind );
|
||||
src = end;
|
||||
if ( *src==L'.' && *(src+1)==L'.' ){
|
||||
src+=2;
|
||||
long l_ind2 = wcstol( src, &end, 10 );
|
||||
if( end==src || errno )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
src = end;
|
||||
|
||||
if( l_ind2 < 0 )
|
||||
{
|
||||
l_ind2 = var_count+l_ind2+1;
|
||||
}
|
||||
int direction = l_ind2<l_ind ? -1 : 1 ;
|
||||
for (long jjj = l_ind; jjj*direction <= l_ind2*direction; jjj+=direction) {
|
||||
// debug(0, L"Expand range [set]: %i\n", jjj);
|
||||
indexes.push_back( jjj );
|
||||
count++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
indexes.push_back( l_ind );
|
||||
count++;
|
||||
}
|
||||
while (iswspace(*src)) src++;
|
||||
}
|
||||
|
||||
@@ -264,6 +288,10 @@ static int update_values( wcstring_list_t &list,
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if ( (size_t)ind >= list.size() )
|
||||
{
|
||||
list.resize( ind+1 );
|
||||
}
|
||||
|
||||
// free((void *) al_get(list, ind));
|
||||
list[ ind ] = newv;
|
||||
@@ -285,7 +313,7 @@ static void erase_values(wcstring_list_t &list, const std::vector<long> &indexes
|
||||
std::set<long>::const_reverse_iterator iter;
|
||||
for (iter = indexes_set.rbegin(); iter != indexes_set.rend(); iter++) {
|
||||
long val = *iter;
|
||||
if (val > 0 && val <= list.size()) {
|
||||
if (val > 0 && (size_t)val <= list.size()) {
|
||||
// One-based indexing!
|
||||
list.erase(list.begin() + val - 1);
|
||||
}
|
||||
@@ -498,7 +526,7 @@ static int builtin_set( parser_t &parser, wchar_t **argv )
|
||||
also specify scope
|
||||
*/
|
||||
|
||||
if( query && (erase || list || global || local || universal || exportv || unexport ) )
|
||||
if( query && (erase || list) )
|
||||
{
|
||||
append_format(stderr_buffer,
|
||||
BUILTIN_ERR_COMBO,
|
||||
@@ -627,7 +655,7 @@ static int builtin_set( parser_t &parser, wchar_t **argv )
|
||||
if( erase )
|
||||
{
|
||||
append_format(stderr_buffer,
|
||||
_(L"%ls: Erase needs a variable name\n%ls\n"),
|
||||
_(L"%ls: Erase needs a variable name\n"),
|
||||
argv[0] );
|
||||
|
||||
builtin_print_help( parser, argv[0], stderr_buffer );
|
||||
@@ -687,7 +715,7 @@ static int builtin_set( parser_t &parser, wchar_t **argv )
|
||||
/*
|
||||
Slice mode
|
||||
*/
|
||||
int idx_count, val_count;
|
||||
size_t idx_count, val_count;
|
||||
wcstring_list_t values;
|
||||
std::vector<long> indexes;
|
||||
wcstring_list_t result;
|
||||
|
||||
17
color.cpp
17
color.cpp
@@ -41,6 +41,12 @@ static int parse_hex_digit(wchar_t x) {
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned long squared_difference(long p1, long p2)
|
||||
{
|
||||
unsigned long diff = (unsigned long)labs(p1 - p2);
|
||||
return diff * diff;
|
||||
}
|
||||
|
||||
static unsigned char convert_color(const unsigned char rgb[3], const uint32_t *colors, size_t color_count) {
|
||||
long r = rgb[0], g = rgb[1], b = rgb[2];
|
||||
unsigned long best_distance = (unsigned long)(-1);
|
||||
@@ -48,10 +54,7 @@ static unsigned char convert_color(const unsigned char rgb[3], const uint32_t *c
|
||||
for (unsigned char idx = 0; idx < color_count; idx++) {
|
||||
uint32_t color = colors[idx];
|
||||
long test_r = (color >> 16) & 0xFF, test_g = (color >> 8) & 0xFF, test_b = (color >> 0) & 0xFF;
|
||||
unsigned long distance = 0;
|
||||
distance += (r - test_r) * (r - test_r);
|
||||
distance += (g - test_g) * (g - test_g);
|
||||
distance += (b - test_b) * (b - test_b);
|
||||
unsigned long distance = squared_difference(r, test_r) + squared_difference(g, test_g) + squared_difference(b, test_b);
|
||||
if (distance <= best_distance) {
|
||||
best_index = idx;
|
||||
best_distance = distance;
|
||||
@@ -161,11 +164,9 @@ static unsigned char term8_color_for_rgb(const unsigned char rgb[3]) {
|
||||
0x000000, //Black
|
||||
0xFF0000, //Red
|
||||
0x00FF00, //Green
|
||||
0x725000, //Brown
|
||||
0xFFFF00, //Yellow
|
||||
0x0000FF, //Blue
|
||||
0xFF00FF, //Magenta
|
||||
0xFF00FF, //Purple
|
||||
0x00FFFF, //Cyan
|
||||
0xFFFFFF, //White
|
||||
};
|
||||
@@ -214,12 +215,14 @@ unsigned char rgb_color_t::to_term256_index() const {
|
||||
}
|
||||
|
||||
unsigned char rgb_color_t::to_name_index() const {
|
||||
assert(type == type_named || type == type_rgb);
|
||||
if (type == type_named) {
|
||||
return data.name_idx;
|
||||
} else if (type == type_rgb) {
|
||||
return term8_color_for_rgb(data.rgb);
|
||||
} else {
|
||||
throw "Bad type for to_name_index";
|
||||
/* This is an error */
|
||||
return (unsigned char)(-1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
258
common.cpp
258
common.cpp
@@ -104,6 +104,10 @@ static struct winsize termsize;
|
||||
|
||||
void show_stackframe()
|
||||
{
|
||||
/* Hack to avoid showing backtraces in the tester */
|
||||
if (program_name && ! wcscmp(program_name, L"(ignore)"))
|
||||
return;
|
||||
|
||||
void *trace[32];
|
||||
char **messages = (char **)NULL;
|
||||
int i, trace_size = 0;
|
||||
@@ -122,90 +126,41 @@ void show_stackframe()
|
||||
}
|
||||
}
|
||||
|
||||
wcstring_list_t completions_to_wcstring_list( const std::vector<completion_t> &list )
|
||||
{
|
||||
wcstring_list_t strings;
|
||||
strings.reserve(list.size());
|
||||
for (std::vector<completion_t>::const_iterator iter = list.begin(); iter != list.end(); ++iter) {
|
||||
strings.push_back(iter->completion);
|
||||
}
|
||||
return strings;
|
||||
}
|
||||
|
||||
|
||||
int fgetws2( wchar_t **b, int *len, FILE *f )
|
||||
int fgetws2(wcstring *s, FILE *f)
|
||||
{
|
||||
int i=0;
|
||||
wint_t c;
|
||||
|
||||
wchar_t *buff = *b;
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
/* Reallocate the buffer if necessary */
|
||||
if( i+1 >= *len )
|
||||
{
|
||||
int new_len = maxi( 128, (*len)*2);
|
||||
buff = (wchar_t *)realloc( buff, sizeof(wchar_t)*new_len );
|
||||
if( buff == 0 )
|
||||
{
|
||||
DIE_MEM();
|
||||
}
|
||||
else
|
||||
{
|
||||
*len = new_len;
|
||||
*b = buff;
|
||||
}
|
||||
}
|
||||
|
||||
errno=0;
|
||||
|
||||
errno=0;
|
||||
|
||||
c = getwc( f );
|
||||
|
||||
if( errno == EILSEQ )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
//fwprintf( stderr, L"b\n" );
|
||||
|
||||
|
||||
switch( c )
|
||||
{
|
||||
/* End of line */
|
||||
case WEOF:
|
||||
case L'\n':
|
||||
case L'\0':
|
||||
buff[i]=L'\0';
|
||||
return i;
|
||||
/* Ignore carriage returns */
|
||||
case L'\r':
|
||||
break;
|
||||
|
||||
default:
|
||||
buff[i++]=c;
|
||||
i++;
|
||||
s->push_back((wchar_t)c);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
static bool string_sort_predicate(const wcstring& d1, const wcstring& d2)
|
||||
{
|
||||
return wcsfilecmp(d1.c_str(), d2.c_str()) < 0;
|
||||
}
|
||||
|
||||
void sort_strings( std::vector<wcstring> &strings)
|
||||
{
|
||||
std::sort(strings.begin(), strings.end(), string_sort_predicate);
|
||||
}
|
||||
|
||||
void sort_completions( std::vector<completion_t> &completions)
|
||||
{
|
||||
std::sort(completions.begin(), completions.end());
|
||||
}
|
||||
wchar_t *str2wcs( const char *in )
|
||||
{
|
||||
wchar_t *out;
|
||||
@@ -240,8 +195,8 @@ wcstring str2wcstring( const std::string &in )
|
||||
wchar_t *str2wcs_internal( const char *in, wchar_t *out )
|
||||
{
|
||||
size_t res=0;
|
||||
int in_pos=0;
|
||||
int out_pos = 0;
|
||||
size_t in_pos=0;
|
||||
size_t out_pos = 0;
|
||||
mbstate_t state;
|
||||
size_t len;
|
||||
|
||||
@@ -341,8 +296,8 @@ std::string wcs2string(const wcstring &input)
|
||||
char *wcs2str_internal( const wchar_t *in, char *out )
|
||||
{
|
||||
size_t res=0;
|
||||
int in_pos=0;
|
||||
int out_pos = 0;
|
||||
size_t in_pos=0;
|
||||
size_t out_pos = 0;
|
||||
mbstate_t state;
|
||||
|
||||
CHECK( in, 0 );
|
||||
@@ -383,8 +338,7 @@ char *wcs2str_internal( const wchar_t *in, char *out )
|
||||
|
||||
char **wcsv2strv( const wchar_t * const *in )
|
||||
{
|
||||
int count =0;
|
||||
int i;
|
||||
size_t i, count = 0;
|
||||
|
||||
while( in[count] != 0 )
|
||||
count++;
|
||||
@@ -507,18 +461,7 @@ int wcsvarchr( wchar_t chr )
|
||||
*/
|
||||
int my_wcswidth( const wchar_t *c )
|
||||
{
|
||||
int res=0;
|
||||
while( *c )
|
||||
{
|
||||
int w = wcwidth( *c++ );
|
||||
if( w < 0 )
|
||||
w = 1;
|
||||
if( w > 2 )
|
||||
w=1;
|
||||
|
||||
res += w;
|
||||
}
|
||||
return res;
|
||||
return fish_wcswidth(c, wcslen(c));
|
||||
}
|
||||
|
||||
wchar_t *quote_end( const wchar_t *pos )
|
||||
@@ -555,7 +498,7 @@ wcstring wsetlocale(int category, const wchar_t *locale)
|
||||
{
|
||||
|
||||
char *lang = NULL;
|
||||
if (locale && wcscmp(locale,L"")){
|
||||
if (locale){
|
||||
lang = wcs2str( locale );
|
||||
}
|
||||
char * res = setlocale(category,lang);
|
||||
@@ -577,7 +520,7 @@ bool contains_internal( const wchar_t *a, ... )
|
||||
{
|
||||
const wchar_t *arg;
|
||||
va_list va;
|
||||
int res = 0;
|
||||
bool res = false;
|
||||
|
||||
CHECK( a, 0 );
|
||||
|
||||
@@ -586,7 +529,7 @@ bool contains_internal( const wchar_t *a, ... )
|
||||
{
|
||||
if( wcscmp( a,arg) == 0 )
|
||||
{
|
||||
res=1;
|
||||
res = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -616,17 +559,17 @@ __sentinel bool contains_internal( const wcstring &needle, ... )
|
||||
return res;
|
||||
}
|
||||
|
||||
int read_blocked(int fd, void *buf, size_t count)
|
||||
long read_blocked(int fd, void *buf, size_t count)
|
||||
{
|
||||
int res;
|
||||
ssize_t res;
|
||||
sigset_t chldset, oldset;
|
||||
|
||||
sigemptyset( &chldset );
|
||||
sigaddset( &chldset, SIGCHLD );
|
||||
sigprocmask(SIG_BLOCK, &chldset, &oldset);
|
||||
VOMIT_ON_FAILURE(pthread_sigmask(SIG_BLOCK, &chldset, &oldset));
|
||||
res = read( fd, buf, count );
|
||||
sigprocmask( SIG_SETMASK, &oldset, 0 );
|
||||
return res;
|
||||
VOMIT_ON_FAILURE(pthread_sigmask(SIG_SETMASK, &oldset, NULL));
|
||||
return res;
|
||||
}
|
||||
|
||||
ssize_t write_loop(int fd, const char *buff, size_t count)
|
||||
@@ -654,7 +597,7 @@ ssize_t write_loop(int fd, const char *buff, size_t count)
|
||||
break;
|
||||
}
|
||||
}
|
||||
return out_cum;
|
||||
return (ssize_t)out_cum;
|
||||
}
|
||||
|
||||
ssize_t read_loop(int fd, void *buff, size_t count)
|
||||
@@ -666,31 +609,54 @@ ssize_t read_loop(int fd, void *buff, size_t count)
|
||||
return result;
|
||||
}
|
||||
|
||||
void debug( int level, const wchar_t *msg, ... )
|
||||
static bool should_debug(int level)
|
||||
{
|
||||
va_list va;
|
||||
|
||||
wcstring sb;
|
||||
|
||||
int errno_old = errno;
|
||||
|
||||
if( level > debug_level )
|
||||
return;
|
||||
return false;
|
||||
|
||||
CHECK( msg, );
|
||||
|
||||
sb = format_string(L"%ls: ", program_name);
|
||||
va_start(va, msg);
|
||||
sb.append(vformat_string(msg, va));
|
||||
va_end(va);
|
||||
/* Hack to not print error messages in the tests */
|
||||
if ( program_name && ! wcscmp(program_name, L"(ignore)") )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void debug_shared( const wcstring &msg )
|
||||
{
|
||||
const wcstring sb = wcstring(program_name) + L": " + msg;
|
||||
wcstring sb2;
|
||||
write_screen( sb, sb2 );
|
||||
fwprintf( stderr, L"%ls", sb2.c_str() );
|
||||
|
||||
errno = errno_old;
|
||||
}
|
||||
|
||||
void debug( int level, const wchar_t *msg, ... )
|
||||
{
|
||||
if (! should_debug(level))
|
||||
return;
|
||||
int errno_old = errno;
|
||||
va_list va;
|
||||
va_start(va, msg);
|
||||
wcstring local_msg = vformat_string(msg, va);
|
||||
va_end(va);
|
||||
debug_shared(local_msg);
|
||||
errno = errno_old;
|
||||
}
|
||||
|
||||
void debug( int level, const char *msg, ... )
|
||||
{
|
||||
if (! should_debug(level))
|
||||
return;
|
||||
int errno_old = errno;
|
||||
char local_msg[512];
|
||||
va_list va;
|
||||
va_start(va, msg);
|
||||
vsnprintf(local_msg, sizeof local_msg, msg, va);
|
||||
va_end(va);
|
||||
debug_shared(str2wcstring(local_msg));
|
||||
errno = errno_old;
|
||||
}
|
||||
|
||||
|
||||
void debug_safe(int level, const char *msg, const char *param1, const char *param2, const char *param3, const char *param4, const char *param5, const char *param6, const char *param7, const char *param8, const char *param9, const char *param10, const char *param11, const char *param12)
|
||||
{
|
||||
const char * const params[] = {param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12};
|
||||
@@ -773,7 +739,7 @@ void format_long_safe(wchar_t buff[128], long val) {
|
||||
while (val > 0) {
|
||||
long rem = val % 10;
|
||||
/* Here we're assuming that wide character digits are contiguous - is that a correct assumption? */
|
||||
buff[idx++] = L'0' + (rem < 0 ? -rem : rem);
|
||||
buff[idx++] = L'0' + (wchar_t)(rem < 0 ? -rem : rem);
|
||||
val /= 10;
|
||||
}
|
||||
if (negative)
|
||||
@@ -815,13 +781,13 @@ void write_screen( const wcstring &msg, wcstring &buff )
|
||||
Check is token is wider than one line.
|
||||
If so we mark it as an overflow and break the token.
|
||||
*/
|
||||
if((tok_width + wcwidth(*pos)) > (screen_width-1))
|
||||
if((tok_width + fish_wcwidth(*pos)) > (screen_width-1))
|
||||
{
|
||||
overflow = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
tok_width += wcwidth( *pos );
|
||||
tok_width += fish_wcwidth( *pos );
|
||||
pos++;
|
||||
}
|
||||
|
||||
@@ -898,13 +864,13 @@ static wchar_t *escape_simple( const wchar_t *in )
|
||||
return out;
|
||||
}
|
||||
|
||||
wchar_t *escape( const wchar_t *in_orig,
|
||||
int flags )
|
||||
wchar_t *escape( const wchar_t *in_orig, escape_flags_t flags )
|
||||
{
|
||||
const wchar_t *in = in_orig;
|
||||
|
||||
int escape_all = flags & ESCAPE_ALL;
|
||||
int no_quoted = flags & ESCAPE_NO_QUOTED;
|
||||
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;
|
||||
@@ -955,8 +921,8 @@ wchar_t *escape( const wchar_t *in_orig,
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
switch( *in )
|
||||
wchar_t c = *in;
|
||||
switch( c )
|
||||
{
|
||||
case L'\t':
|
||||
*(pos++) = L'\\';
|
||||
@@ -1020,9 +986,12 @@ wchar_t *escape( const wchar_t *in_orig,
|
||||
case L'%':
|
||||
case L'~':
|
||||
{
|
||||
need_escape=1;
|
||||
if( escape_all )
|
||||
*pos++ = L'\\';
|
||||
if (! no_tilde || c != L'~')
|
||||
{
|
||||
need_escape=1;
|
||||
if( escape_all )
|
||||
*pos++ = L'\\';
|
||||
}
|
||||
*pos++ = *in;
|
||||
break;
|
||||
}
|
||||
@@ -1076,19 +1045,20 @@ wchar_t *escape( const wchar_t *in_orig,
|
||||
return out;
|
||||
}
|
||||
|
||||
wcstring escape_string( const wcstring &in, int escape_all ) {
|
||||
wchar_t *tmp = escape(in.c_str(), escape_all);
|
||||
wcstring escape_string( const wcstring &in, escape_flags_t flags ) {
|
||||
wchar_t *tmp = escape(in.c_str(), flags);
|
||||
wcstring result(tmp);
|
||||
free(tmp);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
wchar_t *unescape( const wchar_t * orig, int flags )
|
||||
{
|
||||
|
||||
int mode = 0;
|
||||
int in_pos, out_pos, len;
|
||||
int mode = 0;
|
||||
int out_pos;
|
||||
size_t in_pos;
|
||||
size_t len;
|
||||
int c;
|
||||
int bracket_count=0;
|
||||
wchar_t prev=0;
|
||||
@@ -1104,7 +1074,7 @@ wchar_t *unescape( const wchar_t * orig, int flags )
|
||||
if( !in )
|
||||
DIE_MEM();
|
||||
|
||||
for( in_pos=0, out_pos=0;
|
||||
for( in_pos=0, out_pos=0;
|
||||
in_pos<len;
|
||||
(prev=(out_pos>=0)?in[out_pos]:0), out_pos++, in_pos++ )
|
||||
{
|
||||
@@ -1193,6 +1163,8 @@ wchar_t *unescape( const wchar_t * orig, int flags )
|
||||
{
|
||||
base=8;
|
||||
chars=3;
|
||||
// note in_pod must be larger than 0 since we incremented it above
|
||||
assert(in_pos > 0);
|
||||
in_pos--;
|
||||
break;
|
||||
}
|
||||
@@ -1200,7 +1172,7 @@ wchar_t *unescape( const wchar_t * orig, int flags )
|
||||
|
||||
for( i=0; i<chars; i++ )
|
||||
{
|
||||
int d = convert_digit( in[++in_pos],base);
|
||||
long d = convert_digit( in[++in_pos],base);
|
||||
|
||||
if( d < 0 )
|
||||
{
|
||||
@@ -1213,7 +1185,7 @@ wchar_t *unescape( const wchar_t * orig, int flags )
|
||||
|
||||
if( (res <= max_val) )
|
||||
{
|
||||
in[out_pos] = (byte?ENCODE_DIRECT_BASE:0)+res;
|
||||
in[out_pos] = (wchar_t)((byte?ENCODE_DIRECT_BASE:0)+res);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1776,7 +1748,7 @@ wcstring format_size(long long sz)
|
||||
{
|
||||
if( sz < (1024*1024) || !sz_name[i+1] )
|
||||
{
|
||||
int isz = sz/1024;
|
||||
long isz = ((long)sz)/1024;
|
||||
if( isz > 9 )
|
||||
result.append( format_string( L"%d%ls", isz, sz_name[i] ));
|
||||
else
|
||||
@@ -1943,9 +1915,15 @@ void configure_thread_assertions_for_testing(void) {
|
||||
}
|
||||
|
||||
/* Notice when we've forked */
|
||||
static pid_t initial_pid;
|
||||
static pid_t initial_pid = 0;
|
||||
|
||||
/* Be able to restore the term's foreground process group */
|
||||
static pid_t initial_foreground_process_group = -1;
|
||||
|
||||
bool is_forked_child(void) {
|
||||
/* Just bail if nobody's called setup_fork_guards - e.g. fishd */
|
||||
if (! initial_pid) return false;
|
||||
|
||||
bool is_child_of_fork = (getpid() != initial_pid);
|
||||
if (is_child_of_fork) {
|
||||
printf("Uh-oh: %d\n", getpid());
|
||||
@@ -1954,11 +1932,25 @@ bool is_forked_child(void) {
|
||||
return is_child_of_fork;
|
||||
}
|
||||
|
||||
void setup_fork_guards(void) {
|
||||
void setup_fork_guards(void)
|
||||
{
|
||||
/* Notice when we fork by stashing our pid. This seems simpler than pthread_atfork(). */
|
||||
initial_pid = getpid();
|
||||
}
|
||||
|
||||
void save_term_foreground_process_group(void)
|
||||
{
|
||||
initial_foreground_process_group = tcgetpgrp(STDIN_FILENO);
|
||||
}
|
||||
|
||||
void restore_term_foreground_process_group(void)
|
||||
{
|
||||
if (initial_foreground_process_group != -1)
|
||||
{
|
||||
tcsetpgrp(STDIN_FILENO, initial_foreground_process_group);
|
||||
}
|
||||
}
|
||||
|
||||
bool is_main_thread() {
|
||||
assert (main_thread_id != 0);
|
||||
return main_thread_id == pthread_self();
|
||||
@@ -2019,3 +2011,25 @@ scoped_lock::scoped_lock(pthread_mutex_t &mutex) : lock_obj(&mutex), locked(fals
|
||||
scoped_lock::~scoped_lock() {
|
||||
if (locked) this->unlock();
|
||||
}
|
||||
|
||||
wcstokenizer::wcstokenizer(const wcstring &s, const wcstring &separator) :
|
||||
buffer(),
|
||||
str(),
|
||||
state(),
|
||||
sep(separator)
|
||||
{
|
||||
buffer = wcsdup(s.c_str());
|
||||
str = buffer;
|
||||
state = NULL;
|
||||
}
|
||||
|
||||
bool wcstokenizer::next(wcstring &result) {
|
||||
wchar_t *tmp = wcstok(str, sep.c_str(), &state);
|
||||
str = NULL;
|
||||
if (tmp) result = tmp;
|
||||
return tmp != NULL;
|
||||
}
|
||||
|
||||
wcstokenizer::~wcstokenizer() {
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
93
common.h
93
common.h
@@ -63,14 +63,18 @@ typedef std::vector<wcstring> wcstring_list_t;
|
||||
*/
|
||||
#define UNESCAPE_INCOMPLETE 2
|
||||
|
||||
/**
|
||||
Escape all characters, including magic characters like the semicolon
|
||||
*/
|
||||
#define ESCAPE_ALL 1
|
||||
/**
|
||||
Do not try to use 'simplified' quoted escapes, and do not use empty quotes as the empty string
|
||||
*/
|
||||
#define ESCAPE_NO_QUOTED 2
|
||||
/* Flags for the escape() and escape_string() functions */
|
||||
enum {
|
||||
/** Escape all characters, including magic characters like the semicolon */
|
||||
ESCAPE_ALL = 1 << 0,
|
||||
|
||||
/** Do not try to use 'simplified' quoted escapes, and do not use empty quotes as the empty string */
|
||||
ESCAPE_NO_QUOTED = 1 << 1,
|
||||
|
||||
/** Do not escape tildes */
|
||||
ESCAPE_NO_TILDE = 1 << 2
|
||||
};
|
||||
typedef unsigned int escape_flags_t;
|
||||
|
||||
/**
|
||||
Helper macro for errors
|
||||
@@ -118,7 +122,7 @@ extern const wchar_t *program_name;
|
||||
if( !(arg) ) \
|
||||
{ \
|
||||
debug( 0, \
|
||||
_( L"function %s called with null value for argument %s. " ), \
|
||||
"function %s called with null value for argument %s. ", \
|
||||
__func__, \
|
||||
#arg ); \
|
||||
bugreport(); \
|
||||
@@ -131,9 +135,9 @@ extern const wchar_t *program_name;
|
||||
*/
|
||||
#define FATAL_EXIT() \
|
||||
{ \
|
||||
int exit_read_count;char exit_read_buff; \
|
||||
char exit_read_buff; \
|
||||
show_stackframe(); \
|
||||
exit_read_count=read( 0, &exit_read_buff, 1 ); \
|
||||
read( 0, &exit_read_buff, 1 ); \
|
||||
exit_without_destructors( 1 ); \
|
||||
} \
|
||||
|
||||
@@ -144,8 +148,8 @@ extern const wchar_t *program_name;
|
||||
#define DIE_MEM() \
|
||||
{ \
|
||||
fwprintf( stderr, \
|
||||
L"fish: Out of memory on line %d of file %s, shutting down fish\n", \
|
||||
__LINE__, \
|
||||
L"fish: Out of memory on line %ld of file %s, shutting down fish\n", \
|
||||
(long)__LINE__, \
|
||||
__FILE__ ); \
|
||||
FATAL_EXIT(); \
|
||||
}
|
||||
@@ -158,7 +162,7 @@ extern const wchar_t *program_name;
|
||||
if( signal_is_blocked() ) \
|
||||
{ \
|
||||
debug( 0, \
|
||||
_( L"function %s called while blocking signals. " ), \
|
||||
"function %s called while blocking signals. ", \
|
||||
__func__); \
|
||||
bugreport(); \
|
||||
show_stackframe(); \
|
||||
@@ -177,7 +181,7 @@ extern const wchar_t *program_name;
|
||||
#define N_(wstr) wstr
|
||||
|
||||
/**
|
||||
Check if the specified stringelement is a part of the specified string list
|
||||
Check if the specified string element is a part of the specified string list
|
||||
*/
|
||||
#define contains( str,... ) contains_internal( str, __VA_ARGS__, NULL )
|
||||
|
||||
@@ -187,24 +191,16 @@ extern const wchar_t *program_name;
|
||||
void show_stackframe();
|
||||
|
||||
|
||||
wcstring_list_t completions_to_wcstring_list( const std::vector<completion_t> &completions );
|
||||
|
||||
/**
|
||||
Read a line from the stream f into the buffer buff of length len. If
|
||||
buff is to small, it will be reallocated, and both buff and len will
|
||||
be updated to reflect this. Returns the number of bytes read or -1
|
||||
on failiure.
|
||||
Read a line from the stream f into the string. Returns
|
||||
the number of bytes read or -1 on failiure.
|
||||
|
||||
If the carriage return character is encountered, it is
|
||||
ignored. fgetws() considers the line to end if reading the file
|
||||
results in either a newline (L'\n') character, the null (L'\\0')
|
||||
character or the end of file (WEOF) character.
|
||||
*/
|
||||
int fgetws2( wchar_t **buff, int *len, FILE *f );
|
||||
|
||||
void sort_strings( std::vector<wcstring> &strings);
|
||||
|
||||
void sort_completions( std::vector<completion_t> &strings);
|
||||
int fgetws2(wcstring *s, FILE *f);
|
||||
|
||||
/**
|
||||
Returns a newly allocated wide character string equivalent of the
|
||||
@@ -453,6 +449,10 @@ class narrow_string_rep_t {
|
||||
private:
|
||||
const char *str;
|
||||
|
||||
/* No copying */
|
||||
narrow_string_rep_t &operator=(const narrow_string_rep_t &);
|
||||
narrow_string_rep_t(const narrow_string_rep_t &x);
|
||||
|
||||
public:
|
||||
~narrow_string_rep_t() {
|
||||
free((void *)str);
|
||||
@@ -476,6 +476,11 @@ bool is_forked_child();
|
||||
class scoped_lock {
|
||||
pthread_mutex_t *lock_obj;
|
||||
bool locked;
|
||||
|
||||
/* No copying */
|
||||
scoped_lock &operator=(const scoped_lock &);
|
||||
scoped_lock(const scoped_lock &);
|
||||
|
||||
public:
|
||||
void lock(void);
|
||||
void unlock(void);
|
||||
@@ -483,28 +488,19 @@ class scoped_lock {
|
||||
~scoped_lock();
|
||||
};
|
||||
|
||||
/* Wrapper around wcstok */
|
||||
class wcstokenizer {
|
||||
wchar_t *buffer, *str, *state;
|
||||
const wcstring sep;
|
||||
|
||||
public:
|
||||
wcstokenizer(const wcstring &s, const wcstring &separator) : sep(separator) {
|
||||
wchar_t *wcsdup(const wchar_t *s);
|
||||
buffer = wcsdup(s.c_str());
|
||||
str = buffer;
|
||||
state = NULL;
|
||||
}
|
||||
|
||||
bool next(wcstring &result) {
|
||||
wchar_t *tmp = wcstok(str, sep.c_str(), &state);
|
||||
str = NULL;
|
||||
if (tmp) result = tmp;
|
||||
return tmp != NULL;
|
||||
}
|
||||
/* No copying */
|
||||
wcstokenizer &operator=(const wcstokenizer &);
|
||||
wcstokenizer(const wcstokenizer &);
|
||||
|
||||
~wcstokenizer() {
|
||||
free(buffer);
|
||||
}
|
||||
public:
|
||||
wcstokenizer(const wcstring &s, const wcstring &separator);
|
||||
bool next(wcstring &result);
|
||||
~wcstokenizer();
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -595,7 +591,7 @@ __sentinel bool contains_internal( const wcstring &needle, ... );
|
||||
if you _know_ there is data available for reading, or the program
|
||||
will hang until there is data.
|
||||
*/
|
||||
int read_blocked(int fd, void *buf, size_t count);
|
||||
long read_blocked(int fd, void *buf, size_t count);
|
||||
|
||||
/**
|
||||
Loop a write request while failiure is non-critical. Return -1 and set errno
|
||||
@@ -628,6 +624,7 @@ ssize_t read_loop(int fd, void *buff, size_t count);
|
||||
|
||||
will print the string 'fish: Pi = 3.141', given that debug_level is 1 or higher, and that program_name is 'fish'.
|
||||
*/
|
||||
void debug( int level, const char *msg, ... );
|
||||
void debug( int level, const wchar_t *msg, ... );
|
||||
|
||||
/**
|
||||
@@ -639,8 +636,8 @@ void debug( int level, const wchar_t *msg, ... );
|
||||
\return The escaped string, or 0 if there is not enough memory
|
||||
*/
|
||||
|
||||
wchar_t *escape( const wchar_t *in, int escape_all );
|
||||
wcstring escape_string( const wcstring &in, int escape_all );
|
||||
wchar_t *escape( const wchar_t *in, escape_flags_t flags );
|
||||
wcstring escape_string( const wcstring &in, escape_flags_t flags );
|
||||
|
||||
/**
|
||||
Expand backslashed escapes and substitute them with their unescaped
|
||||
@@ -732,6 +729,10 @@ void configure_thread_assertions_for_testing();
|
||||
/** Set up a guard to complain if we try to do certain things (like take a lock) after calling fork */
|
||||
void setup_fork_guards(void);
|
||||
|
||||
/** Save the value of tcgetpgrp so we can restore it on exit */
|
||||
void save_term_foreground_process_group(void);
|
||||
void restore_term_foreground_process_group(void);
|
||||
|
||||
/** Return whether we are the child of a fork */
|
||||
bool is_forked_child(void);
|
||||
void assert_is_not_forked_child(const char *who);
|
||||
|
||||
276
complete.cpp
276
complete.cpp
@@ -251,6 +251,64 @@ const wcstring &completion_entry_t::get_short_opt_str() const {
|
||||
return short_opt_str;
|
||||
}
|
||||
|
||||
/* completion_t functions */
|
||||
completion_t::completion_t(const wcstring &comp, const wcstring &desc, int flags_val) : completion(comp), description(desc), flags(flags_val)
|
||||
{
|
||||
if( flags & COMPLETE_AUTO_SPACE )
|
||||
{
|
||||
flags = flags & ~COMPLETE_AUTO_SPACE;
|
||||
size_t len = completion.size();
|
||||
if (len > 0 && ( wcschr( L"/=@:", comp.at(len-1)) != 0 ))
|
||||
flags |= COMPLETE_NO_SPACE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
completion_t::completion_t(const completion_t &him) : completion(him.completion), description(him.description), flags(him.flags)
|
||||
{
|
||||
}
|
||||
|
||||
completion_t &completion_t::operator=(const completion_t &him)
|
||||
{
|
||||
if (this != &him)
|
||||
{
|
||||
this->completion = him.completion;
|
||||
this->description = him.description;
|
||||
this->flags = him.flags;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool completion_t::operator < (const completion_t& rhs) const
|
||||
{
|
||||
return this->completion < rhs.completion;
|
||||
}
|
||||
|
||||
bool completion_t::operator == (const completion_t& rhs) const
|
||||
{
|
||||
return this->completion == rhs.completion;
|
||||
}
|
||||
|
||||
bool completion_t::operator != (const completion_t& rhs) const
|
||||
{
|
||||
return ! (*this == rhs);
|
||||
}
|
||||
|
||||
wcstring_list_t completions_to_wcstring_list( const std::vector<completion_t> &list )
|
||||
{
|
||||
wcstring_list_t strings;
|
||||
strings.reserve(list.size());
|
||||
for (std::vector<completion_t>::const_iterator iter = list.begin(); iter != list.end(); ++iter) {
|
||||
strings.push_back(iter->completion);
|
||||
}
|
||||
return strings;
|
||||
}
|
||||
|
||||
void sort_completions( std::vector<completion_t> &completions)
|
||||
{
|
||||
std::sort(completions.begin(), completions.end());
|
||||
}
|
||||
|
||||
/** Class representing an attempt to compute completions */
|
||||
class completer_t {
|
||||
const complete_type_t type;
|
||||
@@ -296,15 +354,21 @@ class completer_t {
|
||||
|
||||
void complete_cmd_desc( const wcstring &str );
|
||||
|
||||
bool complete_variable(const wcstring &str, int start_offset);
|
||||
bool complete_variable(const wcstring &str, size_t start_offset);
|
||||
|
||||
bool condition_test( const wcstring &condition );
|
||||
|
||||
void complete_strings( const wcstring &wc_escaped,
|
||||
const wchar_t *desc,
|
||||
wcstring (*desc_func)(const wcstring &),
|
||||
std::vector<completion_t> &possible_comp,
|
||||
complete_flags_t flags );
|
||||
|
||||
expand_flags_t expand_flags() const {
|
||||
/* Never do command substitution in autosuggestions */
|
||||
/* Never do command substitution in autosuggestions. Sadly, we also can't yet do job expansion because it's not thread safe. */
|
||||
expand_flags_t result = 0;
|
||||
if (type == COMPLETE_AUTOSUGGEST)
|
||||
result |= EXPAND_SKIP_CMDSUBST;
|
||||
result |= EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_JOBS;
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -341,7 +405,7 @@ void completion_autoload_t::command_removed(const wcstring &cmd) {
|
||||
Create a new completion entry
|
||||
|
||||
*/
|
||||
void completion_allocate(std::vector<completion_t> &completions, const wcstring &comp, const wcstring &desc, complete_flags_t flags)
|
||||
void append_completion(std::vector<completion_t> &completions, const wcstring &comp, const wcstring &desc, complete_flags_t flags)
|
||||
{
|
||||
completions.push_back(completion_t(comp, desc, flags));
|
||||
}
|
||||
@@ -383,7 +447,7 @@ bool completer_t::condition_test( const wcstring &condition )
|
||||
|
||||
|
||||
/** Search for an exactly matching completion entry. Must be called while locked. */
|
||||
static completion_entry_t *complete_find_exact_entry( const wchar_t *cmd, const bool cmd_is_path )
|
||||
static completion_entry_t *complete_find_exact_entry( const wcstring &cmd, const bool cmd_is_path )
|
||||
{
|
||||
ASSERT_IS_LOCKED(completion_lock);
|
||||
completion_entry_t *result = NULL;
|
||||
@@ -396,7 +460,7 @@ static completion_entry_t *complete_find_exact_entry( const wchar_t *cmd, const
|
||||
}
|
||||
|
||||
/** Locate the specified entry. Create it if it doesn't exist. Must be called while locked. */
|
||||
static completion_entry_t *complete_get_exact_entry( const wchar_t *cmd, bool cmd_is_path )
|
||||
static completion_entry_t *complete_get_exact_entry( const wcstring &cmd, bool cmd_is_path )
|
||||
{
|
||||
ASSERT_IS_LOCKED(completion_lock);
|
||||
completion_entry_t *c;
|
||||
@@ -405,7 +469,7 @@ static completion_entry_t *complete_get_exact_entry( const wchar_t *cmd, bool cm
|
||||
|
||||
if( c == NULL )
|
||||
{
|
||||
c = new completion_entry_t(cmd, cmd_is_path, L"", true);
|
||||
c = new completion_entry_t(cmd, cmd_is_path, L"", false);
|
||||
completion_set.insert(c);
|
||||
}
|
||||
|
||||
@@ -560,7 +624,7 @@ static wcstring format_error(const wchar_t *prefix, const wcstring &str) {
|
||||
Find the full path and commandname from a command string 'str'.
|
||||
*/
|
||||
static void parse_cmd_string(const wcstring &str, wcstring &path, wcstring &cmd) {
|
||||
if (! path_get_path_string(str, path)) {
|
||||
if (! path_get_path(str, &path)) {
|
||||
/** Use the empty string as the 'path' for commands that can not be found. */
|
||||
path = L"";
|
||||
}
|
||||
@@ -574,71 +638,69 @@ static void parse_cmd_string(const wcstring &str, wcstring &path, wcstring &cmd)
|
||||
}
|
||||
}
|
||||
|
||||
int complete_is_valid_option( const wchar_t *str,
|
||||
const wchar_t *opt,
|
||||
int complete_is_valid_option( const wcstring &str,
|
||||
const wcstring &opt,
|
||||
wcstring_list_t *errors,
|
||||
bool allow_autoload )
|
||||
{
|
||||
wcstring cmd, path;
|
||||
int found_match = 0;
|
||||
int authoritative = 1;
|
||||
bool found_match = false;
|
||||
bool authoritative = true;
|
||||
int opt_found=0;
|
||||
std::set<wcstring> gnu_match_set;
|
||||
int is_gnu_opt=0;
|
||||
int is_old_opt=0;
|
||||
int is_short_opt=0;
|
||||
int is_gnu_exact=0;
|
||||
bool is_gnu_opt=false;
|
||||
bool is_old_opt=false;
|
||||
bool is_short_opt=false;
|
||||
bool is_gnu_exact=false;
|
||||
size_t gnu_opt_len=0;
|
||||
|
||||
std::vector<char> short_validated;
|
||||
|
||||
|
||||
CHECK( str, 0 );
|
||||
CHECK( opt, 0 );
|
||||
|
||||
if (opt.empty())
|
||||
return false;
|
||||
|
||||
std::vector<bool> short_validated;
|
||||
/*
|
||||
Check some generic things like -- and - options.
|
||||
*/
|
||||
switch( wcslen(opt ) )
|
||||
switch( opt.size() )
|
||||
{
|
||||
|
||||
case 0:
|
||||
case 1:
|
||||
{
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
case 2:
|
||||
{
|
||||
if( wcscmp( L"--", opt ) == 0 )
|
||||
if( opt == L"--" )
|
||||
{
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( opt[0] != L'-' )
|
||||
if( opt.at(0) != L'-' )
|
||||
{
|
||||
if( errors )
|
||||
errors->push_back(L"Option does not begin with a '-'");
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
short_validated.resize(wcslen(opt), 0);
|
||||
short_validated.resize(opt.size(), 0);
|
||||
|
||||
is_gnu_opt = opt[1]==L'-';
|
||||
is_gnu_opt = opt.at(1) == L'-';
|
||||
if( is_gnu_opt )
|
||||
{
|
||||
const wchar_t *opt_end = wcschr(opt, L'=' );
|
||||
if( opt_end )
|
||||
size_t opt_end = opt.find(L'=');
|
||||
if( opt_end != wcstring::npos )
|
||||
{
|
||||
gnu_opt_len = (opt_end-opt)-2;
|
||||
gnu_opt_len = opt_end-2;
|
||||
}
|
||||
else
|
||||
{
|
||||
gnu_opt_len = wcslen(opt)-2;
|
||||
gnu_opt_len = opt.size() - 2;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -657,18 +719,17 @@ int complete_is_valid_option( const wchar_t *str,
|
||||
{
|
||||
const completion_entry_t *i = *iter;
|
||||
const wcstring &match = i->cmd_is_path ? path : cmd;
|
||||
const wchar_t *a;
|
||||
|
||||
if( !wildcard_match( match, i->cmd ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
found_match = 1;
|
||||
found_match = true;
|
||||
|
||||
if( !i->authoritative )
|
||||
if (! i->authoritative)
|
||||
{
|
||||
authoritative = 0;
|
||||
authoritative = false;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -683,14 +744,12 @@ int complete_is_valid_option( const wchar_t *str,
|
||||
continue;
|
||||
}
|
||||
|
||||
if (wcsncmp(&opt[2], o.long_opt.c_str(), gnu_opt_len) == 0)
|
||||
if (opt.compare(2, gnu_opt_len, o.long_opt) == 0)
|
||||
{
|
||||
gnu_match_set.insert(o.long_opt);
|
||||
if( (wcsncmp( &opt[2],
|
||||
o.long_opt.c_str(),
|
||||
o.long_opt.size())==0) )
|
||||
if (opt.compare(2, o.long_opt.size(), o.long_opt))
|
||||
{
|
||||
is_gnu_exact=1;
|
||||
is_gnu_exact = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -706,10 +765,10 @@ int complete_is_valid_option( const wchar_t *str,
|
||||
continue;
|
||||
|
||||
|
||||
if( wcscmp( &opt[1], o.long_opt.c_str() )==0)
|
||||
if( opt.compare(1, wcstring::npos, o.long_opt )==0)
|
||||
{
|
||||
opt_found = 1;
|
||||
is_old_opt = 1;
|
||||
opt_found = true;
|
||||
is_old_opt = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -718,12 +777,10 @@ int complete_is_valid_option( const wchar_t *str,
|
||||
if( is_old_opt )
|
||||
break;
|
||||
|
||||
|
||||
for( a = &opt[1]; *a; a++ )
|
||||
for (size_t opt_idx = 1; opt_idx < opt.size(); opt_idx++)
|
||||
{
|
||||
|
||||
const wcstring &short_opt_str = i->get_short_opt_str();
|
||||
size_t str_idx = short_opt_str.find(*a);
|
||||
size_t str_idx = short_opt_str.find(opt.at(opt_idx));
|
||||
if (str_idx != wcstring::npos )
|
||||
{
|
||||
if (str_idx + 1 < short_opt_str.size() && short_opt_str.at(str_idx + 1) == L':' )
|
||||
@@ -732,17 +789,13 @@ int complete_is_valid_option( const wchar_t *str,
|
||||
This is a short option with an embedded argument,
|
||||
call complete_is_valid_argument on the argument.
|
||||
*/
|
||||
wchar_t nopt[3];
|
||||
nopt[0]=L'-';
|
||||
nopt[1]=opt[1];
|
||||
nopt[2]=L'\0';
|
||||
|
||||
short_validated.at(a-opt) =
|
||||
complete_is_valid_argument( str, nopt, &opt[2]);
|
||||
const wcstring nopt = L"-" + opt.substr(1, 1);
|
||||
short_validated.at(opt_idx) =
|
||||
complete_is_valid_argument( str, nopt, opt.substr(2));
|
||||
}
|
||||
else
|
||||
{
|
||||
short_validated.at(a-opt)=1;
|
||||
short_validated.at(opt_idx) = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -758,19 +811,15 @@ int complete_is_valid_option( const wchar_t *str,
|
||||
|
||||
if( is_short_opt )
|
||||
{
|
||||
size_t j;
|
||||
|
||||
opt_found=1;
|
||||
for( j=1; j<wcslen(opt); j++)
|
||||
for( size_t j=1; j<opt.size(); j++)
|
||||
{
|
||||
if ( !short_validated.at(j))
|
||||
{
|
||||
if( errors )
|
||||
{
|
||||
wchar_t str[2];
|
||||
str[0] = opt[j];
|
||||
str[1]=0;
|
||||
errors->push_back(format_error(_(L"Unknown option: "), str));
|
||||
const wcstring str = opt.substr(j, 1);
|
||||
errors->push_back(format_error(_(L"Unknown option: "), str.c_str()));
|
||||
}
|
||||
|
||||
opt_found = 0;
|
||||
@@ -799,21 +848,19 @@ int complete_is_valid_option( const wchar_t *str,
|
||||
}
|
||||
}
|
||||
|
||||
return (authoritative && found_match)?opt_found:1;
|
||||
return (authoritative && found_match)?opt_found:true;
|
||||
}
|
||||
|
||||
int complete_is_valid_argument( const wchar_t *str,
|
||||
const wchar_t *opt,
|
||||
const wchar_t *arg )
|
||||
bool complete_is_valid_argument( const wcstring &str, const wcstring &opt, const wcstring &arg )
|
||||
{
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Copy any strings in possible_comp which have the specified prefix
|
||||
to the list comp_out. The prefix may contain wildcards. The output
|
||||
will consist of completion_t structs.
|
||||
to the completer's completion array. The prefix may contain wildcards.
|
||||
The output will consist of completion_t structs.
|
||||
|
||||
There are three ways to specify descriptions for each
|
||||
completion. Firstly, if a description has already been added to the
|
||||
@@ -822,22 +869,20 @@ int complete_is_valid_argument( const wchar_t *str,
|
||||
completion. Thirdly, if none of the above are available, the desc
|
||||
string is used as a description.
|
||||
|
||||
\param comp_out the destination list
|
||||
\param wc_escaped the prefix, possibly containing wildcards. The wildcard should not have been unescaped, i.e. '*' should be used for any string, not the ANY_STRING character.
|
||||
\param desc the default description, used for completions with no embedded description. The description _may_ contain a COMPLETE_SEP character, if not, one will be prefixed to it
|
||||
\param desc_func the function that generates a description for those completions witout an embedded description
|
||||
\param possible_comp the list of possible completions to iterate over
|
||||
*/
|
||||
|
||||
static void complete_strings( std::vector<completion_t> &comp_out,
|
||||
const wcstring &wc_escaped,
|
||||
const wchar_t *desc,
|
||||
wcstring (*desc_func)(const wcstring &),
|
||||
std::vector<completion_t> &possible_comp,
|
||||
complete_flags_t flags )
|
||||
void completer_t::complete_strings( const wcstring &wc_escaped,
|
||||
const wchar_t *desc,
|
||||
wcstring (*desc_func)(const wcstring &),
|
||||
std::vector<completion_t> &possible_comp,
|
||||
complete_flags_t flags )
|
||||
{
|
||||
wcstring tmp = wc_escaped;
|
||||
if (! expand_one(tmp, EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_WILDCARDS))
|
||||
if (! expand_one(tmp, EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_WILDCARDS | this->expand_flags()))
|
||||
return;
|
||||
|
||||
const wchar_t *wc = parse_util_unescape_wildcards( tmp.c_str() );
|
||||
@@ -849,7 +894,7 @@ static void complete_strings( std::vector<completion_t> &comp_out,
|
||||
|
||||
if( next_str )
|
||||
{
|
||||
wildcard_complete( next_str, wc, desc, desc_func, comp_out, flags );
|
||||
wildcard_complete( next_str, wc, desc, desc_func, this->completions, flags );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -865,7 +910,6 @@ void completer_t::complete_cmd_desc( const wcstring &str )
|
||||
ASSERT_IS_MAIN_THREAD();
|
||||
|
||||
const wchar_t *cmd_start;
|
||||
int cmd_len;
|
||||
int skip;
|
||||
|
||||
const wchar_t * const cmd = str.c_str();
|
||||
@@ -876,14 +920,12 @@ void completer_t::complete_cmd_desc( const wcstring &str )
|
||||
else
|
||||
cmd_start = cmd;
|
||||
|
||||
cmd_len = wcslen(cmd_start);
|
||||
|
||||
/*
|
||||
Using apropos with a single-character search term produces far
|
||||
to many results - require at least two characters if we don't
|
||||
know the location of the whatis-database.
|
||||
*/
|
||||
if(cmd_len < 2 )
|
||||
if (wcslen(cmd_start) < 2)
|
||||
return;
|
||||
|
||||
if( wildcard_has( cmd_start, 0 ) )
|
||||
@@ -1009,12 +1051,8 @@ void completer_t::complete_cmd( const wcstring &str_cmd, bool use_function, bool
|
||||
if (str_cmd.empty())
|
||||
return;
|
||||
|
||||
wchar_t *path_cpy;
|
||||
wchar_t *nxt_path;
|
||||
wchar_t *state;
|
||||
std::vector<completion_t> possible_comp;
|
||||
|
||||
|
||||
env_var_t cdpath = env_get_string(L"CDPATH");
|
||||
if (cdpath.missing_or_empty())
|
||||
cdpath = L".";
|
||||
@@ -1043,14 +1081,10 @@ void completer_t::complete_cmd( const wcstring &str_cmd, bool use_function, bool
|
||||
const env_var_t path = env_get_string(L"PATH");
|
||||
if( !path.missing() )
|
||||
{
|
||||
|
||||
path_cpy = wcsdup( path.c_str() );
|
||||
|
||||
for( nxt_path = wcstok( path_cpy, ARRAY_SEP_STR, &state );
|
||||
nxt_path != 0;
|
||||
nxt_path = wcstok( 0, ARRAY_SEP_STR, &state) )
|
||||
wcstring base_path;
|
||||
wcstokenizer tokenizer(path, ARRAY_SEP_STR);
|
||||
while (tokenizer.next(base_path))
|
||||
{
|
||||
wcstring base_path = nxt_path;
|
||||
if (base_path.empty())
|
||||
continue;
|
||||
|
||||
@@ -1078,7 +1112,6 @@ void completer_t::complete_cmd( const wcstring &str_cmd, bool use_function, bool
|
||||
}
|
||||
}
|
||||
}
|
||||
free( path_cpy );
|
||||
if (wants_description)
|
||||
this->complete_cmd_desc( str_cmd );
|
||||
}
|
||||
@@ -1096,7 +1129,7 @@ void completer_t::complete_cmd( const wcstring &str_cmd, bool use_function, bool
|
||||
possible_comp.push_back(completion_t(names.at(i)));
|
||||
}
|
||||
|
||||
complete_strings( this->completions, str_cmd, 0, &complete_function_desc, possible_comp, 0 );
|
||||
this->complete_strings( str_cmd, 0, &complete_function_desc, possible_comp, 0 );
|
||||
}
|
||||
|
||||
possible_comp.clear();
|
||||
@@ -1104,7 +1137,7 @@ void completer_t::complete_cmd( const wcstring &str_cmd, bool use_function, bool
|
||||
if( use_builtin )
|
||||
{
|
||||
builtin_get_names( possible_comp );
|
||||
complete_strings( this->completions, str_cmd, 0, &builtin_get_desc, possible_comp, 0 );
|
||||
this->complete_strings( str_cmd, 0, &builtin_get_desc, possible_comp, 0 );
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1143,7 +1176,7 @@ void completer_t::complete_from_args( const wcstring &str,
|
||||
if (! is_autosuggest)
|
||||
proc_pop_interactive();
|
||||
|
||||
complete_strings( this->completions, str, desc.c_str(), 0, possible_comp, flags );
|
||||
this->complete_strings( str, desc.c_str(), 0, possible_comp, flags );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1409,7 +1442,7 @@ bool completer_t::complete_param( const wcstring &scmd_orig, const wcstring &spo
|
||||
completion[0] = o->short_opt;
|
||||
completion[1] = 0;
|
||||
|
||||
completion_allocate( this->completions, completion, desc, 0 );
|
||||
append_completion( this->completions, completion, desc, 0 );
|
||||
|
||||
}
|
||||
|
||||
@@ -1436,7 +1469,7 @@ bool completer_t::complete_param( const wcstring &scmd_orig, const wcstring &spo
|
||||
int has_arg=0; /* Does this switch have any known arguments */
|
||||
int req_arg=0; /* Does this switch _require_ an argument */
|
||||
|
||||
int offset = 0;
|
||||
size_t offset = 0;
|
||||
complete_flags_t flags = 0;
|
||||
|
||||
|
||||
@@ -1461,14 +1494,14 @@ bool completer_t::complete_param( const wcstring &scmd_orig, const wcstring &spo
|
||||
homebrew getopt-like functions.
|
||||
*/
|
||||
wcstring completion = format_string(L"%ls=", whole_opt.c_str()+offset);
|
||||
completion_allocate( this->completions,
|
||||
append_completion( this->completions,
|
||||
completion,
|
||||
C_(o->desc.c_str()),
|
||||
flags );
|
||||
|
||||
}
|
||||
|
||||
completion_allocate( this->completions,
|
||||
append_completion( this->completions,
|
||||
whole_opt.c_str() + offset,
|
||||
C_(o->desc.c_str()),
|
||||
flags );
|
||||
@@ -1503,8 +1536,9 @@ void completer_t::complete_param_expand( const wcstring &sstr, bool do_file)
|
||||
|
||||
if (! do_file)
|
||||
flags |= EXPAND_SKIP_WILDCARDS;
|
||||
|
||||
if (type == COMPLETE_AUTOSUGGEST)
|
||||
|
||||
/* Squelch file descriptions per issue 254 */
|
||||
if (type == COMPLETE_AUTOSUGGEST || do_file)
|
||||
flags |= EXPAND_NO_DESCRIPTIONS;
|
||||
|
||||
if( expand_string( comp_str,
|
||||
@@ -1525,11 +1559,11 @@ void completer_t::debug_print_completions()
|
||||
/**
|
||||
Complete the specified string as an environment variable
|
||||
*/
|
||||
bool completer_t::complete_variable(const wcstring &str, int start_offset)
|
||||
bool completer_t::complete_variable(const wcstring &str, size_t start_offset)
|
||||
{
|
||||
const wchar_t * const whole_var = str.c_str();
|
||||
const wchar_t *var = &whole_var[start_offset];
|
||||
int varlen = wcslen( var );
|
||||
size_t varlen = wcslen( var );
|
||||
int res = 0;
|
||||
bool wants_description = (type != COMPLETE_AUTOSUGGEST);
|
||||
|
||||
@@ -1537,7 +1571,7 @@ bool completer_t::complete_variable(const wcstring &str, int start_offset)
|
||||
for( size_t i=0; i<names.size(); i++ )
|
||||
{
|
||||
const wcstring & env_name = names.at(i);
|
||||
int namelen = env_name.size();
|
||||
size_t namelen = env_name.size();
|
||||
int match=0, match_no_case=0;
|
||||
|
||||
if( varlen > namelen )
|
||||
@@ -1578,7 +1612,7 @@ bool completer_t::complete_variable(const wcstring &str, int start_offset)
|
||||
desc = format_string(COMPLETE_VAR_DESC_VAL, value.c_str());
|
||||
}
|
||||
|
||||
completion_allocate( this->completions, comp.c_str(), desc.c_str(), flags );
|
||||
append_completion( this->completions, comp.c_str(), desc.c_str(), flags );
|
||||
res =1;
|
||||
|
||||
}
|
||||
@@ -1632,7 +1666,7 @@ bool completer_t::try_complete_user( const wcstring &str )
|
||||
if( name_end == 0 )
|
||||
{
|
||||
struct passwd *pw;
|
||||
int name_len = wcslen( user_name );
|
||||
size_t name_len = wcslen( user_name );
|
||||
|
||||
setpwent();
|
||||
|
||||
@@ -1653,7 +1687,7 @@ bool completer_t::try_complete_user( const wcstring &str )
|
||||
if( wcsncmp( user_name, pw_name, name_len )==0 )
|
||||
{
|
||||
wcstring desc = format_string(COMPLETE_USER_DESC, pw_name);
|
||||
completion_allocate( this->completions,
|
||||
append_completion( this->completions,
|
||||
&pw_name[name_len],
|
||||
desc,
|
||||
COMPLETE_NO_SPACE );
|
||||
@@ -1665,7 +1699,7 @@ bool completer_t::try_complete_user( const wcstring &str )
|
||||
wcstring name = format_string(L"~%ls", pw_name);
|
||||
wcstring desc = format_string(COMPLETE_USER_DESC, pw_name);
|
||||
|
||||
completion_allocate( this->completions,
|
||||
append_completion( this->completions,
|
||||
name,
|
||||
desc,
|
||||
COMPLETE_NO_CASE | COMPLETE_DONT_ESCAPE | COMPLETE_NO_SPACE );
|
||||
@@ -1687,14 +1721,12 @@ void complete( const wcstring &cmd, std::vector<completion_t> &comps, complete_t
|
||||
completer_t completer(cmd, type);
|
||||
|
||||
const wchar_t *tok_begin, *tok_end, *cmdsubst_begin, *cmdsubst_end, *prev_begin, *prev_end;
|
||||
wcstring buff;
|
||||
tokenizer tok;
|
||||
const wchar_t *current_token=0, *prev_token=0;
|
||||
wcstring current_command;
|
||||
int on_command=0;
|
||||
int pos;
|
||||
size_t pos;
|
||||
bool done=false;
|
||||
int cursor_pos;
|
||||
int use_command = 1;
|
||||
int use_function = 1;
|
||||
int use_builtin = 1;
|
||||
@@ -1702,7 +1734,7 @@ void complete( const wcstring &cmd, std::vector<completion_t> &comps, complete_t
|
||||
|
||||
// debug( 1, L"Complete '%ls'", cmd );
|
||||
|
||||
cursor_pos = cmd.size();
|
||||
size_t cursor_pos = cmd.size();
|
||||
|
||||
const wchar_t *cmd_cstr = cmd.c_str();
|
||||
parse_util_cmdsubst_extent( cmd_cstr, cursor_pos, &cmdsubst_begin, &cmdsubst_end );
|
||||
@@ -1727,7 +1759,7 @@ void complete( const wcstring &cmd, std::vector<completion_t> &comps, complete_t
|
||||
{
|
||||
pos = cursor_pos-(cmdsubst_begin-cmd_cstr);
|
||||
|
||||
buff = wcstring( cmdsubst_begin, cmdsubst_end-cmdsubst_begin );
|
||||
wcstring buff = wcstring( cmdsubst_begin, cmdsubst_end-cmdsubst_begin );
|
||||
|
||||
int had_cmd=0;
|
||||
int end_loop=0;
|
||||
@@ -1744,7 +1776,7 @@ void complete( const wcstring &cmd, std::vector<completion_t> &comps, complete_t
|
||||
{
|
||||
|
||||
const wcstring ncmd = tok_last( &tok );
|
||||
int is_ddash = (ncmd == L"--") && ( (tok_get_pos( &tok )+2) < pos );
|
||||
int is_ddash = (ncmd == L"--") && ( (tok_get_pos( &tok )+2) < (long)pos );
|
||||
|
||||
if( !had_cmd )
|
||||
{
|
||||
@@ -1770,11 +1802,9 @@ void complete( const wcstring &cmd, std::vector<completion_t> &comps, complete_t
|
||||
if( !is_ddash ||
|
||||
( (use_command && use_function && use_builtin ) ) )
|
||||
{
|
||||
int token_end;
|
||||
|
||||
current_command = ncmd;
|
||||
|
||||
token_end = tok_get_pos( &tok ) + ncmd.size();
|
||||
size_t token_end = tok_get_pos( &tok ) + ncmd.size();
|
||||
|
||||
on_command = (pos <= token_end );
|
||||
had_cmd=1;
|
||||
@@ -1812,7 +1842,7 @@ void complete( const wcstring &cmd, std::vector<completion_t> &comps, complete_t
|
||||
|
||||
}
|
||||
|
||||
if( tok_get_pos( &tok ) >= pos )
|
||||
if( tok_get_pos( &tok ) >= (long)pos )
|
||||
{
|
||||
end_loop=1;
|
||||
}
|
||||
|
||||
69
complete.h
69
complete.h
@@ -109,7 +109,7 @@ class completion_t
|
||||
|
||||
private:
|
||||
/* No public default constructor */
|
||||
completion_t(){ }
|
||||
completion_t();
|
||||
public:
|
||||
|
||||
/**
|
||||
@@ -134,20 +134,17 @@ class completion_t
|
||||
*/
|
||||
int flags;
|
||||
|
||||
completion_t(const wcstring &comp, const wcstring &desc = L"", int flags_val = 0) : completion(comp), description(desc), flags(flags_val) {
|
||||
if( flags & COMPLETE_AUTO_SPACE )
|
||||
{
|
||||
flags = flags & ~COMPLETE_AUTO_SPACE;
|
||||
size_t len = completion.size();
|
||||
if (len > 0 && ( wcschr( L"/=@:", comp.at(len-1)) != 0 ))
|
||||
flags |= COMPLETE_NO_SPACE;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
bool operator < (const completion_t& rhs) const { return this->completion < rhs.completion; }
|
||||
bool operator == (const completion_t& rhs) const { return this->completion == rhs.completion; }
|
||||
bool operator != (const completion_t& rhs) const { return ! (*this == rhs); }
|
||||
bool is_case_insensitive() const { return !! (flags & COMPLETE_NO_CASE); }
|
||||
|
||||
/* Construction. Note: defining these so that they are not inlined reduces the executable size. */
|
||||
completion_t(const wcstring &comp, const wcstring &desc = L"", int flags_val = 0);
|
||||
completion_t(const completion_t &);
|
||||
completion_t &operator=(const completion_t &);
|
||||
|
||||
/* The following are needed for sorting and uniquing completions */
|
||||
bool operator < (const completion_t& rhs) const;
|
||||
bool operator == (const completion_t& rhs) const;
|
||||
bool operator != (const completion_t& rhs) const;
|
||||
};
|
||||
|
||||
enum complete_type_t {
|
||||
@@ -155,6 +152,12 @@ enum complete_type_t {
|
||||
COMPLETE_AUTOSUGGEST
|
||||
};
|
||||
|
||||
/** Given a list of completions, returns a list of their completion fields */
|
||||
wcstring_list_t completions_to_wcstring_list( const std::vector<completion_t> &completions );
|
||||
|
||||
/** Sorts a list of completions */
|
||||
void sort_completions( std::vector<completion_t> &completions);
|
||||
|
||||
/**
|
||||
|
||||
Add a completion.
|
||||
@@ -198,15 +201,15 @@ enum complete_type_t {
|
||||
\param flags A set of completion flags
|
||||
*/
|
||||
void complete_add( const wchar_t *cmd,
|
||||
bool cmd_is_path,
|
||||
wchar_t short_opt,
|
||||
const wchar_t *long_opt,
|
||||
int long_mode,
|
||||
int result_mode,
|
||||
const wchar_t *condition,
|
||||
const wchar_t *comp,
|
||||
const wchar_t *desc,
|
||||
int flags );
|
||||
bool cmd_is_path,
|
||||
wchar_t short_opt,
|
||||
const wchar_t *long_opt,
|
||||
int long_mode,
|
||||
int result_mode,
|
||||
const wchar_t *condition,
|
||||
const wchar_t *comp,
|
||||
const wchar_t *desc,
|
||||
int flags );
|
||||
/**
|
||||
Sets whether the completion list for this command is complete. If
|
||||
true, any options not matching one of the provided options will be
|
||||
@@ -218,9 +221,9 @@ void complete_set_authoritative( const wchar_t *cmd, bool cmd_type, bool authori
|
||||
Remove a previously defined completion
|
||||
*/
|
||||
void complete_remove( const wchar_t *cmd,
|
||||
bool cmd_is_path,
|
||||
wchar_t short_opt,
|
||||
const wchar_t *long_opt );
|
||||
bool cmd_is_path,
|
||||
wchar_t short_opt,
|
||||
const wchar_t *long_opt );
|
||||
|
||||
|
||||
/** Find all completions of the command cmd, insert them into out. If to_load is not NULL, append all commands that we would autoload, but did not (presumably because this is not the main thread) */
|
||||
@@ -236,8 +239,8 @@ void complete_print( wcstring &out );
|
||||
/**
|
||||
Tests if the specified option is defined for the specified command
|
||||
*/
|
||||
int complete_is_valid_option( const wchar_t *str,
|
||||
const wchar_t *opt,
|
||||
int complete_is_valid_option( const wcstring &str,
|
||||
const wcstring &opt,
|
||||
wcstring_list_t *inErrorsOrNull,
|
||||
bool allow_autoload );
|
||||
|
||||
@@ -245,9 +248,9 @@ int complete_is_valid_option( const wchar_t *str,
|
||||
Tests if the specified argument is valid for the specified option
|
||||
and command
|
||||
*/
|
||||
int complete_is_valid_argument( const wchar_t *str,
|
||||
const wchar_t *opt,
|
||||
const wchar_t *arg );
|
||||
bool complete_is_valid_argument( const wcstring &str,
|
||||
const wcstring &opt,
|
||||
const wcstring &arg );
|
||||
|
||||
|
||||
/**
|
||||
@@ -270,7 +273,7 @@ void complete_load( const wcstring &cmd, bool reload );
|
||||
\param flags completion flags
|
||||
|
||||
*/
|
||||
void completion_allocate(std::vector<completion_t> &completions, const wcstring &comp, const wcstring &desc, int flags);
|
||||
void append_completion(std::vector<completion_t> &completions, const wcstring &comp, const wcstring &desc = L"", int flags = 0);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
15
configure.ac
15
configure.ac
@@ -9,7 +9,7 @@
|
||||
# configure the build process.
|
||||
#
|
||||
|
||||
AC_INIT(fish,1.23.1,fish-users@lists.sf.net)
|
||||
AC_INIT(fish,2.0.0,fish-users@lists.sf.net)
|
||||
|
||||
#
|
||||
# preserve configure arguments for xsel
|
||||
@@ -39,6 +39,8 @@ AC_SUBST(XSEL_MAN)
|
||||
AC_SUBST(XSEL_BIN)
|
||||
AC_SUBST(XSEL_MAN_PATH)
|
||||
|
||||
|
||||
|
||||
#
|
||||
# If needed, run autoconf to regenerate the configure file
|
||||
#
|
||||
@@ -104,8 +106,9 @@ fi
|
||||
# This mostly helps OS X users, since fink usually installs out of
|
||||
# tree and doesn't update CFLAGS.
|
||||
#
|
||||
# It also helps FreeBSD which puts libiconv in /usr/local/lib
|
||||
|
||||
for i in /usr/pkg /sw /opt /opt/local; do
|
||||
for i in /usr/pkg /sw /opt /opt/local /usr/local; do
|
||||
|
||||
AC_MSG_CHECKING([for $i/include include directory])
|
||||
if test -d $i/include; then
|
||||
@@ -119,7 +122,7 @@ for i in /usr/pkg /sw /opt /opt/local; do
|
||||
AC_MSG_CHECKING([for $i/lib library directory])
|
||||
if test -d $i/lib; then
|
||||
AC_MSG_RESULT(yes)
|
||||
LDFLAGS="$LDFLAGS -L$i/lib/ -R$i/lib/"
|
||||
LDFLAGS="$LDFLAGS -L$i/lib/ -Wl,-rpath,$i/lib/"
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
@@ -512,7 +515,7 @@ LIBS="$LIBS_SHARED"
|
||||
if test x$local_gettext != xno; then
|
||||
AC_SEARCH_LIBS( gettext, intl,,)
|
||||
fi
|
||||
AC_SEARCH_LIBS( iconv_open, iconv, , [AC_MSG_ERROR([Could not find an iconv implementation, needed to build fish])] )
|
||||
AC_SEARCH_LIBS( iconv_open, iconv, , [AC_SEARCH_LIBS( libiconv_open, iconv, , [AC_MSG_ERROR([Could not find an iconv implementation, needed to build fish])] )] )
|
||||
LIBS_FISH_PAGER=$LIBS
|
||||
LIBS=$LIBS_COMMON
|
||||
|
||||
@@ -525,7 +528,7 @@ LIBS="$LIBS_SHARED"
|
||||
if test x$local_gettext != xno; then
|
||||
AC_SEARCH_LIBS( gettext, intl,,)
|
||||
fi
|
||||
AC_SEARCH_LIBS( iconv_open, iconv, , [AC_MSG_ERROR([Could not find an iconv implementation, needed to build fish])] )
|
||||
AC_SEARCH_LIBS( iconv_open, iconv, , [AC_SEARCH_LIBS( libiconv_open, iconv, , [AC_MSG_ERROR([Could not find an iconv implementation, needed to build fish])] )] )
|
||||
LIBS_FISHD=$LIBS
|
||||
LIBS=$LIBS_COMMON
|
||||
|
||||
@@ -558,7 +561,7 @@ LIBS=$LIBS_COMMON
|
||||
# Check presense of various header files
|
||||
#
|
||||
|
||||
AC_CHECK_HEADERS([getopt.h termio.h sys/resource.h term.h ncurses/term.h ncurses.h curses.h stropts.h siginfo.h sys/select.h sys/ioctl.h sys/termios.h libintl.h execinfo.h])
|
||||
AC_CHECK_HEADERS([getopt.h termio.h sys/resource.h term.h ncurses/term.h ncurses.h curses.h stropts.h siginfo.h sys/select.h sys/ioctl.h sys/termios.h libintl.h execinfo.h spawn.h])
|
||||
|
||||
AC_CHECK_HEADER(
|
||||
[regex.h],
|
||||
|
||||
@@ -45,6 +45,9 @@ bind to the function name. This way it becomes significantly easier to
|
||||
test the function while editing, and the result is usually more
|
||||
readable as well.
|
||||
|
||||
If you want to autoload bindings each time you start shell, you should
|
||||
define them inside fish_user_key_bindings function.
|
||||
|
||||
- <tt>-a</tt> or <tt>--all</tt> If --key-names is specified, show all key names, not only the ones that actually are defined for the current terminal. If erase mode is specified, this switch will cause all current bindings to be erased.
|
||||
- <tt>-e</tt> or <tt>--erase</tt> Erase mode. All non-switch arguments are interpreted as character sequences and any commands associated with those sequences are erased.
|
||||
- <tt>-h</tt> or <tt>--help</tt> Display help and exit
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
\subsection contains-description Description
|
||||
|
||||
- \c -i or \c --index print the the word index
|
||||
- \c -h or \c --help display this message
|
||||
|
||||
Test if the set VALUES contains the string KEY. Return status is 0 if
|
||||
|
||||
16
doc_src/echo.txt
Normal file
16
doc_src/echo.txt
Normal file
@@ -0,0 +1,16 @@
|
||||
\section echo echo - display a line of text
|
||||
|
||||
\subsection echo-synopsis Synopsis
|
||||
<tt>echo [STRING]</tt>
|
||||
|
||||
\subsection echo-description Description
|
||||
|
||||
Display a line of text.
|
||||
|
||||
- \c -n, \c Do not output a newline
|
||||
- \c -s, \c Do not separate arguments with spaces
|
||||
- \c -h, \c --help Display this help
|
||||
|
||||
\subsection echo-example Example
|
||||
|
||||
<tt>echo 'Hello World'</tt> Print hello world to stdout
|
||||
@@ -1,6 +1,6 @@
|
||||
\section fish_prompt fish_prompt - define the apperance of the command line prompt
|
||||
|
||||
\subsection fish_promt-synopsis Synopsis
|
||||
\subsection fish_prompt-synopsis Synopsis
|
||||
<pre>function fish_prompt
|
||||
...
|
||||
end</pre>
|
||||
@@ -11,6 +11,9 @@ By defining the \c fish_prompt function, the user can choose a custom
|
||||
prompt. The \c fish_prompt function is executed when the prompt is to
|
||||
be shown, and the output is used as a prompt.
|
||||
|
||||
Please keep in mind that the function is executed by <a
|
||||
href='index.html#expand-command-substitution'>command substitution</a>, and so the exit status of commands within fish_prompt will not modify the <a href="index.html#variables-status">$status</a> seen outside of fish_prompt.
|
||||
|
||||
\subsection fish_prompt-example Example
|
||||
|
||||
A simple prompt:
|
||||
|
||||
23
doc_src/fish_right_prompt.txt
Normal file
23
doc_src/fish_right_prompt.txt
Normal file
@@ -0,0 +1,23 @@
|
||||
\section fish_right_prompt fish_right_prompt - define the apperance of the right-side command line prompt
|
||||
|
||||
\subsection fish_right_prompt-synopsis Synopsis
|
||||
<pre>function fish_right_prompt
|
||||
...
|
||||
end</pre>
|
||||
|
||||
\subsection fish_right_prompt-description Description
|
||||
|
||||
\c fish_right_prompt is similar to \c fish_prompt, except that it appears on the right side of the terminal window.
|
||||
|
||||
Multiple lines are not supported in \c fish_right_prompt.
|
||||
|
||||
\subsection fish_prompt-example Example
|
||||
|
||||
A simple right prompt:
|
||||
|
||||
<pre>
|
||||
function fish_right_prompt -d "Write out the right prompt"
|
||||
date "+%m/%d/%y"
|
||||
end
|
||||
</pre>
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
\section funced funced - edit a function interactively
|
||||
|
||||
\subsection funced-synopsis Synopsis
|
||||
<code>funced NAME</code>
|
||||
<code>funced [OPTIONS] NAME</code>
|
||||
|
||||
\subsection funced-description Description
|
||||
|
||||
Use the funced command to interactively edit the definition of a
|
||||
function. If there is no function with the name specified, a skeleton function is inserted, if a function exist, the definion will be shown on the command line.
|
||||
Use the funced command to edit the definition of a
|
||||
function. If there is no function with the name specified, a skeleton function is inserted, if a function exist, the definition will be shown in your editor or on the command line.
|
||||
|
||||
By default, funced edits functions using the text editor in your $EDITOR variable, if set; otherwise it uses the built-in editor.
|
||||
|
||||
- <code>-e command</code> or <code>--editor command</code> Open the function body inside the text editor given by the command (for example, "vi"). The command 'fish' will use the built-in editor.
|
||||
- <code>-i</code> or <code>--interactive</code> Open function body in built-in editor.
|
||||
|
||||
@@ -3,34 +3,34 @@
|
||||
\subsection history-synopsis Synopsis
|
||||
<pre>
|
||||
history (--save | --clear)
|
||||
history (--search | --delete ) (--prefix "prefix string" | --search "search string")
|
||||
history (--search | --delete ) (--prefix "prefix string" | --contains "search string")
|
||||
</pre>
|
||||
|
||||
\subsection history-description Description
|
||||
|
||||
history is used to list, search and delete user's command history.
|
||||
history is used to list, search and delete user's command history
|
||||
|
||||
\subsection history-examples Example
|
||||
|
||||
<pre>
|
||||
history --save
|
||||
Save all changes in history file.
|
||||
Save all changes in history file
|
||||
|
||||
history --clear
|
||||
Delete all history items.
|
||||
Delete all history items
|
||||
|
||||
history --search --contains "foo"
|
||||
Searches commands containing "foo" string.
|
||||
Searches commands containing "foo" string
|
||||
|
||||
history --search --prefix "foo"
|
||||
Searches for commands with prefix "foo".
|
||||
Searches for commands with prefix "foo"
|
||||
|
||||
history --delete --contains "foo"
|
||||
Interactively delete commands containing string "foo".
|
||||
Interactively delete commands containing string "foo"
|
||||
|
||||
history --delete --prefix "foo"
|
||||
Interactively delete commands with prefix "foo".
|
||||
Interactively delete commands with prefix "foo"
|
||||
|
||||
history --delete "foo"
|
||||
Delete command "foo" from history.
|
||||
<pre>
|
||||
Delete command "foo" from history
|
||||
</pre>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
\section if if - conditionally execute a command
|
||||
|
||||
\subsection if-synopsis Synopsis
|
||||
<tt>if CONDITION; COMMANDS_TRUE...; [else; COMMANDS_FALSE...;] end</tt>
|
||||
<tt>if CONDITION; COMMANDS_TRUE...; [else if CONDITION2; COMMANDS_TRUE2...;] [else; COMMANDS_FALSE...;] end</tt>
|
||||
|
||||
\subsection if-description Description
|
||||
|
||||
@@ -24,10 +24,13 @@ variable.
|
||||
<pre>
|
||||
if test -f foo.txt
|
||||
echo foo.txt exists
|
||||
else if test -f bar.txt
|
||||
echo bar.txt exists
|
||||
else
|
||||
echo foo.txt does not exist
|
||||
echo foo.txt and bar.txt do not exist
|
||||
end
|
||||
</pre>
|
||||
will print <tt>foo.txt exists</tt> if the file foo.txt
|
||||
</pre>will print <tt>foo.txt exists</tt> if the file foo.txt
|
||||
exists and is a regular file, otherwise it will print
|
||||
<tt>foo.txt does not exist</tt>.
|
||||
<tt>bar.txt exists</tt> if the file bar.txt exists
|
||||
and is a regular file, otherwise it will print
|
||||
<tt>foo.txt and bar.txt do not exist</tt>.
|
||||
|
||||
@@ -11,7 +11,7 @@ This is the documentation for \c fish, the friendly interactive
|
||||
shell. \c fish is a user friendly commandline shell intended
|
||||
mostly for interactive use. A shell is a program used to execute other
|
||||
programs. For the latest information on \c fish, please visit the <a
|
||||
href="http://www.fishshell.org"><code>fish</code> homepage</a>.
|
||||
href="http://ridiculousfish.com/shell/"><code>fish</code> homepage</a>.
|
||||
|
||||
\section syntax Syntax overview
|
||||
|
||||
@@ -80,7 +80,7 @@ while '-f' will turn it off.
|
||||
|
||||
\subsection quotes Quotes
|
||||
|
||||
Sometimes features such as <a href="#globbing">parameter expansion</a>
|
||||
Sometimes features such as <a href="#expand">parameter expansion</a>
|
||||
and <a href="#escapes">character escapes</a> get in the way. When that
|
||||
happens, the user can write a parameter within quotes, either '
|
||||
(single quote) or " (double quote). There is one important difference
|
||||
@@ -124,7 +124,7 @@ these characters, so called escape sequences are provided. These are:
|
||||
- <code>'\\*'</code>, escapes the star character
|
||||
- <code>'\\?'</code>, escapes the question mark character
|
||||
- <code>'\\~'</code>, escapes the tilde character
|
||||
- <code>'\\%'</code>, escapes the percent character
|
||||
- <code>'\\%%'</code>, escapes the percent character
|
||||
- <code>'\\#'</code>, escapes the hash character
|
||||
- <code>'\\('</code>, escapes the left parenthesis character
|
||||
- <code>'\\)'</code>, escapes the right parenthesis character
|
||||
@@ -581,6 +581,9 @@ A command substitution will not change the value of the <a
|
||||
href='#variables-status'>status</a> variable outside of the command
|
||||
substitution.
|
||||
|
||||
Only part of the output can be used, see <a href='#expand-index-range'>index
|
||||
range expansion</a> for details.
|
||||
|
||||
Example:
|
||||
|
||||
The command <code>echo (basename image.jpg .jpg).png</code> will
|
||||
@@ -674,6 +677,50 @@ element of the foo variable should be dereferenced and never that the fifth
|
||||
element of the doubly dereferenced variable foo. The latter can
|
||||
instead be expressed as $$foo[1][5].
|
||||
|
||||
\subsection expand-index-range Index range expansion
|
||||
|
||||
Both command substitution and environment variables support accessing only
|
||||
specific items by providing a set of indices in square brackets. It's
|
||||
often needed to access a sequence of elements. To do this, one can use
|
||||
range operator '..' for this. A range 'a..b', where range limits 'a' and 'b'
|
||||
are integer numbers, is expanded into a sequence of indices
|
||||
'a a+1 a+2 ... b' or 'a a-1 a-2 ... b' depending on which of 'a' or 'b'
|
||||
is higher. The negative range limits are calculated from the end of the array
|
||||
or command substitution.
|
||||
|
||||
Some examples:
|
||||
<pre>
|
||||
# Limit the command substitution output
|
||||
echo (seq 10)[2..5] # will use elements from 2 to 5
|
||||
# Output is:
|
||||
# 2 3 4 5
|
||||
|
||||
# Use overlapping ranges:
|
||||
echo (seq 10)[2..5 1..3] # will take elements from 2 to 5 and then elements from 1 to 3
|
||||
# Output is:
|
||||
# 2 3 4 5 1 2 3
|
||||
|
||||
# Reverse output
|
||||
echo (seq 10)[-1..1] # will use elements from the last output line to the first one in reverse direction
|
||||
# Output is:
|
||||
# 10 9 8 7 6 5 4 3 2 1
|
||||
</pre>
|
||||
|
||||
The same works when setting or expanding variables:
|
||||
<pre>
|
||||
# Reverse path variable
|
||||
set PATH $PATH[-1..1]
|
||||
# or
|
||||
set PATH[-1..1] $PATH
|
||||
|
||||
# Use only n last items of the PATH
|
||||
set n -3
|
||||
echo $PATH[$n..-1]
|
||||
</pre>
|
||||
|
||||
NOTE: Currently variables are allowed inside variables index expansion, but not in indices,
|
||||
used for command substitution.
|
||||
|
||||
\subsection expand-home Home directory expansion
|
||||
|
||||
The ~ (tilde) character at the beginning of a parameter, followed by a
|
||||
@@ -909,6 +956,9 @@ If you specify a negative index when expanding or assigning to an
|
||||
array variable, the index will be calculated from the end of the
|
||||
array. For example, the index -1 means the last index of an array.
|
||||
|
||||
A range of indices can be specified, see <a href='#expand-index-range'>index
|
||||
range expansion</a> for details.
|
||||
|
||||
\subsection variables-special Special variables
|
||||
|
||||
The user can change the settings of \c fish by changing the values of
|
||||
@@ -1004,6 +1054,7 @@ highlighting in the completion pager:
|
||||
- \c fish_pager_color_completion, the color of the completion itself
|
||||
- \c fish_pager_color_description, the color of the completion description
|
||||
- \c fish_pager_color_progress, the color of the progress bar at the bottom left corner
|
||||
- \c fish_pager_color_secondary, the background color of the every second completion
|
||||
|
||||
Example:
|
||||
|
||||
@@ -1227,8 +1278,8 @@ fish_color_substitution, \c fish_color_redirection, \c fish_color_end,
|
||||
\c fish_color_error, \c fish_color_param, \c fish_color_comment, \c
|
||||
fish_color_match, \c fish_color_search_match, \c fish_color_cwd, \c
|
||||
fish_pager_color_prefix, \c fish_pager_color_completion, \c
|
||||
fish_pager_color_description and \c
|
||||
fish_pager_color_progress. Usually, the value of these variables will
|
||||
fish_pager_color_description, \c fish_pager_color_progress
|
||||
and \c fish_pager_color_secondary. Usually, the value of these variables will
|
||||
be one of \c black, \c red, \c green, \c brown, \c yellow, \c blue, \c
|
||||
magenta, \c purple, \c cyan, \c white or \c normal, but they can be an
|
||||
array containing any color options for the set_color command.
|
||||
@@ -1323,7 +1374,7 @@ translated, a future version of fish should also include translated
|
||||
manuals.
|
||||
|
||||
To make a translation of fish, you will first need the source code,
|
||||
available from the <a href='http://www.fishshell.org'>fish
|
||||
available from the <a href='http://ridiculousfish.com/shell/'>fish
|
||||
homepage</a>. Download the latest version, and then extract it using a
|
||||
command like <code>tar -zxf fish-VERSION.tar.gz</code>.
|
||||
|
||||
|
||||
8
doc_src/pwd.txt
Normal file
8
doc_src/pwd.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
\section pwd pwd - returns the current directory
|
||||
|
||||
\subsection pwd-synopsis Synopsis
|
||||
<tt>pwd</tt>
|
||||
|
||||
\subsection pwd-description Description
|
||||
|
||||
Returns the current directory. Note that fish always resolves symbolic links in the current directory path.
|
||||
@@ -9,8 +9,8 @@ The <tt>read</tt> builtin causes fish to read one line from standard
|
||||
input and store the result in one or more environment variables.
|
||||
|
||||
- <tt>-c CMD</tt> or <tt>--command=CMD</tt> specifies that the initial string in the interactive mode command buffer should be CMD.
|
||||
- <tt>-e</tt> or <tt>--export</tt> specifies that the variables will be exported to subshells.
|
||||
- <tt>-g</tt> or <tt>--global</tt> specifies that the variables will be made global.
|
||||
- <tt>-l</tt> or <tt>--local</tt> specifies that the variables will be made local.
|
||||
- <tt>-m NAME</tt> or <tt>--mode-name=NAME</tt> specifies that the name NAME should be used to save/load the history file. If NAME is fish, the regular fish history will be available.
|
||||
- <tt>-p PROMPT_CMD</tt> or <tt>--prompt=PROMPT_CMD</tt> specifies that the output of the shell command PROMPT_CMD should be used as the prompt for the interactive mode prompt. The default prompt command is <tt>set_color green; echo read; set_color normal; echo "> "</tt>.
|
||||
- <code>-s</code> or <code>--shell</code> Use syntax highlighting, tab completions and command termination suitable for entering shellscript code
|
||||
|
||||
@@ -22,7 +22,7 @@ execute. If something goes wrong while opening or reading the file,
|
||||
|
||||
\subsection source-example Example
|
||||
|
||||
<tt>. ~/.fish</tt>
|
||||
<tt>. ~/.config/fish/config.fish</tt>
|
||||
|
||||
causes fish to reread its initialization file.
|
||||
|
||||
|
||||
43
doc_src/test.txt
Normal file
43
doc_src/test.txt
Normal file
@@ -0,0 +1,43 @@
|
||||
\section test test - perform tests on files and text
|
||||
|
||||
\subsection test-synopsis Synopsis
|
||||
<tt>test [EXPRESSION]</tt>
|
||||
|
||||
\subsection test-description Description
|
||||
|
||||
Tests the expression given and returns true or false.
|
||||
|
||||
- \c -h, \c Display this help
|
||||
- \c -G, \c File owned by effective group ID
|
||||
- \c -L, \c File is syslink
|
||||
- \c -O, \c File owned by effective user ID
|
||||
- \c -S, \c File is socket
|
||||
- \c -a, \c Logical and
|
||||
- \c -b, \c File is block device
|
||||
- \c -c, \c File is character device
|
||||
- \c -d, \c File is a directory
|
||||
- \c -e, \c File exists
|
||||
- \c -f, \c File is regular
|
||||
- \c -f, \c File is set-group-ID
|
||||
- \c -k, \c File has sticky bit set
|
||||
- \c -n, \c String length is non-zero
|
||||
- \c -o, \c Logical or
|
||||
- \c -p, \c File is named pipe
|
||||
- \c -r, \c File is readable
|
||||
- \c -s, \c File size is non-zero
|
||||
- \c -t, \c FD is terminal
|
||||
- \c -u, \c File set-user-ID bit is set
|
||||
- \c -w, \c File is writable
|
||||
- \c -x, \c File is executable
|
||||
- \c -z, \c String length is zero
|
||||
|
||||
\subsection test-example Example
|
||||
|
||||
<pre>
|
||||
if test -d "/"
|
||||
echo "Fish is cool"
|
||||
end
|
||||
</pre>
|
||||
|
||||
Because "/" is a directory the expression will evaluate
|
||||
to true, and "Fish is cool" will be echoed
|
||||
216
env.cpp
216
env.cpp
@@ -63,13 +63,19 @@
|
||||
/**
|
||||
Command used to start fishd
|
||||
*/
|
||||
#define FISHD_CMD L"fishd ^/tmp/fishd.log.%s"
|
||||
#define FISHD_CMD L"fishd ^ /tmp/fishd.log.%s"
|
||||
|
||||
/**
|
||||
Value denoting a null string
|
||||
*/
|
||||
#define ENV_NULL L"\x1d"
|
||||
|
||||
/** Some configuration path environment variables */
|
||||
#define FISH_DATADIR_VAR L"__fish_datadir"
|
||||
#define FISH_SYSCONFDIR_VAR L"__fish_sysconfdir"
|
||||
#define FISH_HELPDIR_VAR L"__fish_help_dir"
|
||||
#define FISH_BIN_DIR L"__fish_bin_dir"
|
||||
|
||||
/**
|
||||
At init, we read all the environment variables from this array.
|
||||
*/
|
||||
@@ -95,6 +101,7 @@ struct var_entry_t
|
||||
typedef std::map<wcstring, var_entry_t*> var_table_t;
|
||||
|
||||
bool g_log_forks = false;
|
||||
bool g_use_posix_spawn = false; //will usually be set to true
|
||||
|
||||
|
||||
/**
|
||||
@@ -181,7 +188,11 @@ static null_terminated_array_t<char> export_array;
|
||||
Flag for checking if we need to regenerate the exported variable
|
||||
array
|
||||
*/
|
||||
static bool has_changed = true;
|
||||
static bool has_changed_exported = true;
|
||||
static void mark_changed_exported()
|
||||
{
|
||||
has_changed_exported = true;
|
||||
}
|
||||
|
||||
/**
|
||||
This string is used to store the value of dynamically
|
||||
@@ -235,10 +246,23 @@ static void start_fishd()
|
||||
debug( 0, _( L"Could not get user information" ) );
|
||||
return;
|
||||
}
|
||||
|
||||
wcstring cmd = format_string(FISHD_CMD, pw->pw_name);
|
||||
|
||||
wcstring cmd = format_string(FISHD_CMD, pw->pw_name);
|
||||
|
||||
/* Prefer the fishd in __fish_bin_dir, if exists */
|
||||
const env_var_t bin_dir = env_get_string(L"__fish_bin_dir");
|
||||
if (! bin_dir.missing_or_empty())
|
||||
{
|
||||
wcstring path = bin_dir + L"/fishd";
|
||||
if (waccess(path, X_OK) == 0)
|
||||
{
|
||||
/* The path command just looks like 'fishd', so insert the bin path to make it absolute */
|
||||
cmd.insert(0, bin_dir + L"/");
|
||||
}
|
||||
}
|
||||
|
||||
parser_t &parser = parser_t::principal_parser();
|
||||
parser.eval( cmd, 0, TOP );
|
||||
parser.eval( cmd, io_chain_t(), TOP );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -351,7 +375,7 @@ static void react_to_variable_change(const wcstring &key) {
|
||||
Universal variable callback function. This function makes sure the
|
||||
proper events are triggered when an event occurs.
|
||||
*/
|
||||
static void universal_callback( int type,
|
||||
static void universal_callback( fish_message_type_t type,
|
||||
const wchar_t *name,
|
||||
const wchar_t *val )
|
||||
{
|
||||
@@ -371,11 +395,14 @@ static void universal_callback( int type,
|
||||
str=L"ERASE";
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if( str )
|
||||
{
|
||||
has_changed=true;
|
||||
mark_changed_exported();
|
||||
|
||||
event_t ev = event_t::variable_event(name);
|
||||
ev.arguments.reset(new wcstring_list_t());
|
||||
@@ -510,7 +537,7 @@ static bool variable_can_be_array(const wchar_t *key) {
|
||||
}
|
||||
}
|
||||
|
||||
void env_init()
|
||||
void env_init(const struct config_paths_t *paths /* or NULL */)
|
||||
{
|
||||
char **p;
|
||||
struct passwd *pw;
|
||||
@@ -530,6 +557,7 @@ void env_init()
|
||||
L"COLUMNS",
|
||||
L"PWD",
|
||||
L"SHLVL",
|
||||
L"FISH_VERSION",
|
||||
};
|
||||
for (size_t i=0; i < sizeof ro_keys / sizeof *ro_keys; i++) {
|
||||
env_read_only.insert(ro_keys[i]);
|
||||
@@ -560,7 +588,7 @@ void env_init()
|
||||
Now the environemnt variable handling is set up, the next step
|
||||
is to insert valid data
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
Import environment variables
|
||||
*/
|
||||
@@ -600,6 +628,15 @@ void env_init()
|
||||
free(key);
|
||||
}
|
||||
|
||||
/* Set the given paths in the environment, if we have any */
|
||||
if (paths != NULL)
|
||||
{
|
||||
env_set(FISH_DATADIR_VAR, paths->data.c_str(), ENV_GLOBAL | ENV_EXPORT);
|
||||
env_set(FISH_SYSCONFDIR_VAR, paths->sysconf.c_str(), ENV_GLOBAL | ENV_EXPORT);
|
||||
env_set(FISH_HELPDIR_VAR, paths->doc.c_str(), ENV_GLOBAL | ENV_EXPORT);
|
||||
env_set(FISH_BIN_DIR, paths->bin.c_str(), ENV_GLOBAL | ENV_EXPORT);
|
||||
}
|
||||
|
||||
/*
|
||||
Set up the PATH variable
|
||||
*/
|
||||
@@ -617,10 +654,11 @@ void env_init()
|
||||
}
|
||||
|
||||
/*
|
||||
Set up the version variable
|
||||
Set up the version variables
|
||||
*/
|
||||
version = str2wcs( PACKAGE_VERSION );
|
||||
env_set( L"version", version, ENV_GLOBAL );
|
||||
env_set( L"FISH_VERSION", version, ENV_GLOBAL );
|
||||
free( version );
|
||||
|
||||
const env_var_t fishd_dir_wstr = env_get_string( L"FISHD_SOCKET_DIR");
|
||||
@@ -654,6 +692,10 @@ void env_init()
|
||||
/* Set g_log_forks */
|
||||
env_var_t log_forks = env_get_string(L"fish_log_forks");
|
||||
g_log_forks = ! log_forks.missing_or_empty() && from_string<bool>(log_forks);
|
||||
|
||||
/* Set g_use_posix_spawn. Default to true. */
|
||||
env_var_t use_posix_spawn = env_get_string(L"fish_use_posix_spawn");
|
||||
g_use_posix_spawn = (use_posix_spawn.missing_or_empty() ? true : from_string<bool>(use_posix_spawn));
|
||||
}
|
||||
|
||||
void env_destroy()
|
||||
@@ -674,7 +716,7 @@ void env_destroy()
|
||||
var_entry_t *entry = iter->second;
|
||||
if( entry->exportv )
|
||||
{
|
||||
has_changed = true;
|
||||
mark_changed_exported();
|
||||
}
|
||||
|
||||
delete entry;
|
||||
@@ -713,12 +755,12 @@ int env_set(const wcstring &key, const wchar_t *val, int var_mode)
|
||||
{
|
||||
ASSERT_IS_MAIN_THREAD();
|
||||
env_node_t *node = NULL;
|
||||
bool has_changed_old = has_changed;
|
||||
bool has_changed_old = has_changed_exported;
|
||||
bool has_changed_new = false;
|
||||
var_entry_t *e=0;
|
||||
int done=0;
|
||||
|
||||
int is_universal = 0;
|
||||
int is_universal = 0;
|
||||
|
||||
if( val && contains( key, L"PWD", L"HOME" ) )
|
||||
{
|
||||
@@ -738,15 +780,14 @@ int env_set(const wcstring &key, const wchar_t *val, int var_mode)
|
||||
if (key == L"umask")
|
||||
{
|
||||
wchar_t *end;
|
||||
int mask;
|
||||
|
||||
|
||||
/*
|
||||
Set the new umask
|
||||
*/
|
||||
if( val && wcslen(val) )
|
||||
{
|
||||
errno=0;
|
||||
mask = wcstol( val, &end, 8 );
|
||||
long mask = wcstol( val, &end, 8 );
|
||||
|
||||
if( !errno && (!*end) && (mask <= 0777) && (mask >= 0) )
|
||||
{
|
||||
@@ -901,15 +942,15 @@ int env_set(const wcstring &key, const wchar_t *val, int var_mode)
|
||||
}
|
||||
|
||||
entry->val = val;
|
||||
|
||||
node->env.insert(std::pair<wcstring, var_entry_t*>(key, entry));
|
||||
node->env[key] = entry;
|
||||
|
||||
if( entry->exportv )
|
||||
{
|
||||
node->exportv=1;
|
||||
}
|
||||
|
||||
has_changed = has_changed_old || has_changed_new;
|
||||
if (has_changed_old || has_changed_new)
|
||||
mark_changed_exported();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -956,7 +997,7 @@ static int try_remove( env_node_t *n,
|
||||
|
||||
if( v->exportv )
|
||||
{
|
||||
has_changed = true;
|
||||
mark_changed_exported();
|
||||
}
|
||||
|
||||
n->env.erase(result);
|
||||
@@ -1057,15 +1098,15 @@ env_var_t env_get_string( const wcstring &key )
|
||||
}
|
||||
else if( key == L"COLUMNS" )
|
||||
{
|
||||
return to_string((long)common_get_width());
|
||||
return to_string(common_get_width());
|
||||
}
|
||||
else if( key == L"LINES" )
|
||||
{
|
||||
return to_string((long)common_get_width());
|
||||
return to_string(common_get_width());
|
||||
}
|
||||
else if( key == L"status" )
|
||||
{
|
||||
return to_string((long)proc_get_last_status());
|
||||
return to_string(proc_get_last_status());
|
||||
}
|
||||
else if( key == L"umask" )
|
||||
{
|
||||
@@ -1148,36 +1189,46 @@ int env_exist( const wchar_t *key, int mode )
|
||||
{
|
||||
if( is_read_only(key) || is_electric(key) )
|
||||
{
|
||||
//Such variables are never exported
|
||||
if (mode & ENV_EXPORT)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else if (mode & ENV_UNEXPORT)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if( ! (mode & ENV_UNIVERSAL) )
|
||||
if( !(mode & ENV_UNIVERSAL) )
|
||||
{
|
||||
env = (mode & ENV_GLOBAL)?global_env:top;
|
||||
|
||||
while( env != 0 )
|
||||
{
|
||||
var_table_t::iterator result = env->env.find( key );
|
||||
|
||||
if ( result != env->env.end() )
|
||||
{
|
||||
res = result->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
res = 0;
|
||||
}
|
||||
|
||||
if( res != 0 )
|
||||
{
|
||||
return 1;
|
||||
if (mode & ENV_EXPORT)
|
||||
{
|
||||
return res->exportv == 1;
|
||||
}
|
||||
else if (mode & ENV_UNEXPORT)
|
||||
{
|
||||
return res->exportv == 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
if( mode & ENV_LOCAL )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if ( mode & ENV_LOCAL )
|
||||
break;
|
||||
|
||||
if( env->new_scope )
|
||||
{
|
||||
env = global_env;
|
||||
@@ -1188,8 +1239,8 @@ int env_exist( const wchar_t *key, int mode )
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( ! (mode & ENV_LOCAL) && ! (mode & ENV_GLOBAL) )
|
||||
|
||||
if( !(mode & ENV_LOCAL) && !(mode & ENV_GLOBAL) )
|
||||
{
|
||||
if( ! get_proc_had_barrier())
|
||||
{
|
||||
@@ -1198,10 +1249,23 @@ int env_exist( const wchar_t *key, int mode )
|
||||
}
|
||||
|
||||
item = env_universal_get( key );
|
||||
|
||||
}
|
||||
return item != 0;
|
||||
|
||||
if (item != NULL)
|
||||
{
|
||||
if (mode & ENV_EXPORT)
|
||||
{
|
||||
return env_universal_get_export(key) == 1;
|
||||
}
|
||||
else if (mode & ENV_UNEXPORT)
|
||||
{
|
||||
return env_universal_get_export(key) == 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1230,7 +1294,8 @@ void env_push( int new_scope )
|
||||
|
||||
if( new_scope )
|
||||
{
|
||||
has_changed |= local_scope_exports(top);
|
||||
if (local_scope_exports(top))
|
||||
mark_changed_exported();
|
||||
}
|
||||
top = node;
|
||||
|
||||
@@ -1258,7 +1323,8 @@ void env_pop()
|
||||
|
||||
if( killme->new_scope )
|
||||
{
|
||||
has_changed |= killme->exportv || local_scope_exports( killme->next );
|
||||
if (killme->exportv || local_scope_exports( killme->next ))
|
||||
mark_changed_exported();
|
||||
}
|
||||
|
||||
top = top->next;
|
||||
@@ -1269,7 +1335,7 @@ void env_pop()
|
||||
var_entry_t *entry = iter->second;
|
||||
if( entry->exportv )
|
||||
{
|
||||
has_changed = true;
|
||||
mark_changed_exported();
|
||||
}
|
||||
delete entry;
|
||||
}
|
||||
@@ -1397,7 +1463,8 @@ static void get_exported( const env_node_t *n, std::map<wcstring, wcstring> &h )
|
||||
var_entry_t *val_entry = iter->second;
|
||||
if( val_entry->exportv && (val_entry->val != ENV_NULL ) )
|
||||
{
|
||||
h.insert(std::pair<wcstring, wcstring>(key, val_entry->val));
|
||||
// Don't use std::map::insert here, since we need to overwrite existing values from previous scopes
|
||||
h[key] = val_entry->val;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1439,7 +1506,7 @@ static void update_export_array_if_necessary(bool recalc) {
|
||||
env_universal_barrier();
|
||||
}
|
||||
|
||||
if( has_changed )
|
||||
if( has_changed_exported )
|
||||
{
|
||||
std::map<wcstring, wcstring> vals;
|
||||
size_t i;
|
||||
@@ -1455,54 +1522,73 @@ static void update_export_array_if_necessary(bool recalc) {
|
||||
const wcstring &key = uni.at(i);
|
||||
const wchar_t *val = env_universal_get( key.c_str() );
|
||||
|
||||
std::map<wcstring, wcstring>::iterator result = vals.find( key );
|
||||
if( wcscmp( val, ENV_NULL) && ( result == vals.end() ) )
|
||||
{
|
||||
if (wcscmp( val, ENV_NULL)) {
|
||||
// Note that std::map::insert does NOT overwrite a value already in the map,
|
||||
// which we depend on here
|
||||
vals.insert(std::pair<wcstring, wcstring>(key, val));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::vector<std::string> local_export_buffer;
|
||||
export_func(vals, local_export_buffer );
|
||||
export_array.set(local_export_buffer);
|
||||
has_changed=false;
|
||||
has_changed_exported=false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
char **env_export_arr( int recalc )
|
||||
char **env_export_arr( bool recalc )
|
||||
{
|
||||
ASSERT_IS_MAIN_THREAD();
|
||||
update_export_array_if_necessary(recalc != 0);
|
||||
update_export_array_if_necessary(recalc);
|
||||
return export_array.get();
|
||||
}
|
||||
|
||||
void env_export_arr(bool recalc, null_terminated_array_t<char> &output)
|
||||
{
|
||||
ASSERT_IS_MAIN_THREAD();
|
||||
update_export_array_if_necessary(recalc != 0);
|
||||
update_export_array_if_necessary(recalc);
|
||||
output = export_array;
|
||||
}
|
||||
|
||||
env_vars::env_vars(const wchar_t * const *keys)
|
||||
env_vars_snapshot_t::env_vars_snapshot_t(const wchar_t * const *keys)
|
||||
{
|
||||
ASSERT_IS_MAIN_THREAD();
|
||||
wcstring key;
|
||||
for (size_t i=0; keys[i]; i++) {
|
||||
const env_var_t val = env_get_string(keys[i]);
|
||||
if (!val.missing()) {
|
||||
vars[keys[i]] = val;
|
||||
key.assign(keys[i]);
|
||||
const env_var_t val = env_get_string(key);
|
||||
if (! val.missing()) {
|
||||
vars[key] = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
env_vars::env_vars() { }
|
||||
env_vars_snapshot_t::env_vars_snapshot_t() { }
|
||||
|
||||
const wchar_t *env_vars::get(const wchar_t *key) const
|
||||
/* The "current" variables are not a snapshot at all, but instead trampoline to env_get_string, etc. We identify the current snapshot based on pointer values. */
|
||||
static const env_vars_snapshot_t sCurrentSnapshot;
|
||||
const env_vars_snapshot_t &env_vars_snapshot_t::current()
|
||||
{
|
||||
std::map<wcstring, wcstring>::const_iterator iter = vars.find(key);
|
||||
return (iter == vars.end() ? NULL : iter->second.c_str());
|
||||
return sCurrentSnapshot;
|
||||
}
|
||||
|
||||
const wchar_t * const env_vars::highlighting_keys[] = {L"PATH", L"CDPATH", L"HIGHLIGHT_DELAY", L"fish_function_path", NULL};
|
||||
bool env_vars_snapshot_t::is_current() const
|
||||
{
|
||||
return this == &sCurrentSnapshot;
|
||||
}
|
||||
|
||||
env_var_t env_vars_snapshot_t::get(const wcstring &key) const
|
||||
{
|
||||
/* If we represent the current state, bounce to env_get_string */
|
||||
if (this->is_current())
|
||||
{
|
||||
return env_get_string(key);
|
||||
}
|
||||
else {
|
||||
std::map<wcstring, wcstring>::const_iterator iter = vars.find(key);
|
||||
return (iter == vars.end() ? env_var_t::missing_var() : env_var_t(iter->second));
|
||||
}
|
||||
}
|
||||
|
||||
const wchar_t * const env_vars_snapshot_t::highlighting_keys[] = {L"PATH", L"CDPATH", L"fish_function_path", NULL};
|
||||
|
||||
29
env.h
29
env.h
@@ -52,11 +52,20 @@ enum{
|
||||
}
|
||||
;
|
||||
|
||||
/* A struct of configuration directories, determined in main() that fish will optionally pass to env_init.
|
||||
*/
|
||||
struct config_paths_t
|
||||
{
|
||||
wcstring data; // e.g. /usr/local/share
|
||||
wcstring sysconf; // e.g. /usr/local/etc
|
||||
wcstring doc; // e.g. /usr/local/share/doc/fish
|
||||
wcstring bin; // e.g. /usr/local/bin
|
||||
};
|
||||
|
||||
/**
|
||||
Initialize environment variable data
|
||||
*/
|
||||
void env_init();
|
||||
void env_init(const struct config_paths_t *paths = NULL);
|
||||
|
||||
/**
|
||||
Destroy environment variable data
|
||||
@@ -156,7 +165,7 @@ void env_push( int new_scope );
|
||||
void env_pop();
|
||||
|
||||
/** Returns an array containing all exported variables in a format suitable for execv. */
|
||||
char **env_export_arr( int recalc );
|
||||
char **env_export_arr( bool recalc );
|
||||
void env_export_arr(bool recalc, null_terminated_array_t<char> &result);
|
||||
|
||||
/**
|
||||
@@ -164,20 +173,26 @@ void env_export_arr(bool recalc, null_terminated_array_t<char> &result);
|
||||
*/
|
||||
wcstring_list_t env_get_names( int flags );
|
||||
|
||||
|
||||
|
||||
/**
|
||||
Update the PWD variable
|
||||
directory
|
||||
*/
|
||||
int env_set_pwd();
|
||||
|
||||
class env_vars {
|
||||
class env_vars_snapshot_t {
|
||||
std::map<wcstring, wcstring> vars;
|
||||
bool is_current() const;
|
||||
|
||||
public:
|
||||
env_vars(const wchar_t * const * keys);
|
||||
env_vars(void);
|
||||
env_vars_snapshot_t(const wchar_t * const * keys);
|
||||
env_vars_snapshot_t(void);
|
||||
|
||||
const wchar_t *get(const wchar_t *key) const;
|
||||
env_var_t get(const wcstring &key) const;
|
||||
|
||||
// Returns the fake snapshot representing the live variables array
|
||||
static const env_vars_snapshot_t ¤t();
|
||||
|
||||
// vars necessary for highlighting
|
||||
static const wchar_t * const highlighting_keys[];
|
||||
@@ -186,5 +201,7 @@ class env_vars {
|
||||
extern bool g_log_forks;
|
||||
extern int g_fork_count;
|
||||
|
||||
extern bool g_use_posix_spawn;
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -61,7 +61,7 @@ static int get_socket_count = 0;
|
||||
static wchar_t * path;
|
||||
static wchar_t *user;
|
||||
static void (*start_fishd)();
|
||||
static void (*external_callback)( int type, const wchar_t *name, const wchar_t *val );
|
||||
static void (*external_callback)( fish_message_type_t type, const wchar_t *name, const wchar_t *val );
|
||||
|
||||
/**
|
||||
Flag set to 1 when a barrier reply is recieved
|
||||
@@ -165,7 +165,7 @@ static int get_socket( int fork_ok )
|
||||
/**
|
||||
Callback function used whenever a new fishd message is recieved
|
||||
*/
|
||||
static void callback( int type, const wchar_t *name, const wchar_t *val )
|
||||
static void callback( fish_message_type_t type, const wchar_t *name, const wchar_t *val )
|
||||
{
|
||||
if( type == BARRIER_REPLY )
|
||||
{
|
||||
@@ -250,7 +250,7 @@ static void reconnect()
|
||||
void env_universal_init( wchar_t * p,
|
||||
wchar_t *u,
|
||||
void (*sf)(),
|
||||
void (*cb)( int type, const wchar_t *name, const wchar_t *val ))
|
||||
void (*cb)( fish_message_type_t type, const wchar_t *name, const wchar_t *val ))
|
||||
{
|
||||
path=p;
|
||||
user=u;
|
||||
|
||||
@@ -20,7 +20,7 @@ extern connection_t env_universal_server;
|
||||
void env_universal_init( wchar_t * p,
|
||||
wchar_t *u,
|
||||
void (*sf)(),
|
||||
void (*cb)( int type, const wchar_t *name, const wchar_t *val ));
|
||||
void (*cb)( fish_message_type_t type, const wchar_t *name, const wchar_t *val ));
|
||||
/**
|
||||
Free memory used by envuni
|
||||
*/
|
||||
|
||||
@@ -95,7 +95,7 @@ typedef struct var_uni_entry
|
||||
{
|
||||
int exportv; /**< Whether the variable should be exported */
|
||||
wcstring val; /**< The value of the variable */
|
||||
var_uni_entry():exportv(0) { }
|
||||
var_uni_entry():exportv(0), val() { }
|
||||
}
|
||||
var_uni_entry_t;
|
||||
|
||||
@@ -106,12 +106,13 @@ static void parse_message( wchar_t *msg,
|
||||
/**
|
||||
The table of all universal variables
|
||||
*/
|
||||
std::map<wcstring, var_uni_entry_t*> env_universal_var;
|
||||
typedef std::map<wcstring, var_uni_entry_t*> env_var_table_t;
|
||||
env_var_table_t env_universal_var;
|
||||
|
||||
/**
|
||||
Callback function, should be called on all events
|
||||
*/
|
||||
void (*callback)( int type,
|
||||
static void (*callback)( fish_message_type_t type,
|
||||
const wchar_t *key,
|
||||
const wchar_t *val );
|
||||
|
||||
@@ -167,6 +168,26 @@ static const char *iconv_wide_names_2[]=
|
||||
}
|
||||
;
|
||||
|
||||
template<class T>
|
||||
class sloppy {};
|
||||
|
||||
static size_t hack_iconv(iconv_t cd, const char * const* inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft)
|
||||
{
|
||||
/* FreeBSD has this prototype: size_t iconv (iconv_t, const char **...)
|
||||
OS X and Linux this one: size_t iconv (iconv_t, char **...)
|
||||
AFAIK there's no single type that can be passed as both char ** and const char **.
|
||||
Therefore, we let C++ figure it out, by providing a struct with an implicit conversion to both char** and const char **.
|
||||
*/
|
||||
struct sloppy_char
|
||||
{
|
||||
const char * const * t;
|
||||
operator char** () const { return (char **)t; }
|
||||
operator const char** () const { return (const char**)t; }
|
||||
} slop_inbuf = {inbuf};
|
||||
|
||||
return iconv( cd, slop_inbuf, inbytesleft, outbuf, outbytesleft );
|
||||
}
|
||||
|
||||
/**
|
||||
Convert utf-8 string to wide string
|
||||
*/
|
||||
@@ -246,7 +267,12 @@ static wchar_t *utf2wcs( const char *in )
|
||||
return 0;
|
||||
}
|
||||
|
||||
nconv = iconv( cd, (char **)&in, &in_len, &nout, &out_len );
|
||||
/* FreeBSD has this prototype: size_t iconv (iconv_t, const char **...)
|
||||
OS X and Linux this one: size_t iconv (iconv_t, char **...)
|
||||
AFAIK there's no single type that can be passed as both char ** and const char **.
|
||||
So we cast the function pointer instead (!)
|
||||
*/
|
||||
nconv = hack_iconv( cd, &in, &in_len, &nout, &out_len );
|
||||
|
||||
if (nconv == (size_t) -1)
|
||||
{
|
||||
@@ -280,6 +306,8 @@ static wchar_t *utf2wcs( const char *in )
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
Convert wide string to utf-8
|
||||
*/
|
||||
@@ -357,7 +385,7 @@ static char *wcs2utf( const wchar_t *in )
|
||||
return 0;
|
||||
}
|
||||
|
||||
nconv = iconv( cd, &char_in, &in_len, &nout, &out_len );
|
||||
nconv = hack_iconv( cd, &char_in, &in_len, &nout, &out_len );
|
||||
|
||||
|
||||
if (nconv == (size_t) -1)
|
||||
@@ -377,7 +405,7 @@ static char *wcs2utf( const wchar_t *in )
|
||||
|
||||
|
||||
|
||||
void env_universal_common_init( void (*cb)(int type, const wchar_t *key, const wchar_t *val ) )
|
||||
void env_universal_common_init( void (*cb)(fish_message_type_t type, const wchar_t *key, const wchar_t *val ) )
|
||||
{
|
||||
callback = cb;
|
||||
}
|
||||
@@ -385,7 +413,7 @@ void env_universal_common_init( void (*cb)(int type, const wchar_t *key, const w
|
||||
|
||||
void env_universal_common_destroy()
|
||||
{
|
||||
std::map<wcstring, var_uni_entry_t*>::iterator iter;
|
||||
env_var_table_t::iterator iter;
|
||||
|
||||
for(iter = env_universal_var.begin(); iter != env_universal_var.end(); ++iter)
|
||||
{
|
||||
@@ -403,9 +431,7 @@ static int read_byte( connection_t *src )
|
||||
if( src->buffer_consumed >= src->buffer_used )
|
||||
{
|
||||
|
||||
int res;
|
||||
|
||||
res = read( src->fd, src->buffer, ENV_UNIVERSAL_BUFFER_SIZE );
|
||||
ssize_t res = read( src->fd, src->buffer, ENV_UNIVERSAL_BUFFER_SIZE );
|
||||
|
||||
// debug(4, L"Read chunk '%.*s'", res, src->buffer );
|
||||
|
||||
@@ -518,7 +544,7 @@ void read_message( connection_t *src )
|
||||
*/
|
||||
void env_universal_common_remove( const wcstring &name )
|
||||
{
|
||||
std::map<wcstring, var_uni_entry_t*>::iterator result = env_universal_var.find(name);
|
||||
env_var_table_t::iterator result = env_universal_var.find(name);
|
||||
if (result != env_universal_var.end())
|
||||
{
|
||||
var_uni_entry_t* v = result->second;
|
||||
@@ -555,7 +581,7 @@ void env_universal_common_set( const wchar_t *key, const wchar_t *val, int expor
|
||||
entry->val = val;
|
||||
env_universal_common_remove( key );
|
||||
|
||||
env_universal_var.insert( std::pair<wcstring, var_uni_entry_t*>(key, entry));
|
||||
env_universal_var[key] = entry;
|
||||
if( callback )
|
||||
{
|
||||
callback( exportv?SET_EXPORT:SET, key, val );
|
||||
@@ -566,7 +592,7 @@ void env_universal_common_set( const wchar_t *key, const wchar_t *val, int expor
|
||||
/**
|
||||
Parse message msg
|
||||
*/
|
||||
static void parse_message( wchar_t *msg,
|
||||
static void parse_message( wchar_t *msg,
|
||||
connection_t *src )
|
||||
{
|
||||
// debug( 3, L"parse_message( %ls );", msg );
|
||||
@@ -596,7 +622,8 @@ static void parse_message( wchar_t *msg,
|
||||
val = tmp+1;
|
||||
val = unescape( val, 0 );
|
||||
|
||||
env_universal_common_set( key, val, exportv );
|
||||
if (key && val)
|
||||
env_universal_common_set( key, val, exportv );
|
||||
|
||||
free( val );
|
||||
free( key );
|
||||
@@ -662,17 +689,17 @@ static int try_send( message_t *msg,
|
||||
{
|
||||
|
||||
debug( 3,
|
||||
L"before write of %d chars to fd %d", strlen(msg->body), fd );
|
||||
L"before write of %d chars to fd %d", msg->body.size(), fd );
|
||||
|
||||
int res = write( fd, msg->body, strlen(msg->body) );
|
||||
ssize_t res = write( fd, msg->body.c_str(), msg->body.size() );
|
||||
|
||||
if( res != -1 )
|
||||
{
|
||||
debug( 4, L"Wrote message '%s'", msg->body );
|
||||
debug( 4, L"Wrote message '%s'", msg->body.c_str() );
|
||||
}
|
||||
else
|
||||
{
|
||||
debug( 4, L"Failed to write message '%s'", msg->body );
|
||||
debug( 4, L"Failed to write message '%s'", msg->body.c_str() );
|
||||
}
|
||||
|
||||
if( res == -1 )
|
||||
@@ -696,7 +723,7 @@ static int try_send( message_t *msg,
|
||||
|
||||
if( !msg->count )
|
||||
{
|
||||
free( msg );
|
||||
delete msg;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@@ -726,6 +753,15 @@ void try_send_all( connection_t *c )
|
||||
}
|
||||
}
|
||||
|
||||
/* The universal variable format has some funny escaping requirements; here we try to be safe */
|
||||
static bool is_universal_safe_to_encode_directly(wchar_t c)
|
||||
{
|
||||
if (c < 32 || c > 128)
|
||||
return false;
|
||||
|
||||
return iswalnum(c) || wcschr(L"/", c);
|
||||
}
|
||||
|
||||
/**
|
||||
Escape specified string
|
||||
*/
|
||||
@@ -734,37 +770,60 @@ static wcstring full_escape( const wchar_t *in )
|
||||
wcstring out;
|
||||
for( ; *in; in++ )
|
||||
{
|
||||
if( *in < 32 )
|
||||
wchar_t c = *in;
|
||||
if (is_universal_safe_to_encode_directly(c))
|
||||
{
|
||||
out.push_back(c);
|
||||
}
|
||||
else if (c < 256)
|
||||
{
|
||||
append_format( out, L"\\x%.2x", *in );
|
||||
append_format(out, L"\\x%.2x", c);
|
||||
}
|
||||
else if( *in < 128 )
|
||||
else if (c < 65536)
|
||||
{
|
||||
out.push_back(*in);
|
||||
}
|
||||
else if( *in < 65536 )
|
||||
{
|
||||
append_format( out, L"\\u%.4x", *in );
|
||||
append_format(out, L"\\u%.4x", c);
|
||||
}
|
||||
else
|
||||
{
|
||||
append_format( out, L"\\U%.8x", *in );
|
||||
append_format(out, L"\\U%.8x", c);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
message_t *create_message( int type,
|
||||
const wchar_t *key_in,
|
||||
const wchar_t *val_in )
|
||||
/* Sets the body of a message to the null-terminated list of null terminated const char *. */
|
||||
void set_body(message_t *msg, ...)
|
||||
{
|
||||
message_t *msg=0;
|
||||
/* Start by counting the length of all the strings */
|
||||
size_t body_len = 0;
|
||||
const char *arg;
|
||||
va_list arg_list;
|
||||
va_start(arg_list, msg);
|
||||
while ((arg = va_arg(arg_list, const char *)) != NULL)
|
||||
body_len += strlen(arg);
|
||||
va_end(arg_list);
|
||||
|
||||
/* Reserve that length in the string */
|
||||
msg->body.reserve(body_len + 1); //+1 for trailing NULL? Do I need that?
|
||||
|
||||
/* Set the string contents */
|
||||
va_start(arg_list, msg);
|
||||
while ((arg = va_arg(arg_list, const char *)) != NULL)
|
||||
msg->body.append(arg);
|
||||
va_end(arg_list);
|
||||
}
|
||||
|
||||
/* Returns an instance of message_t allocated via new */
|
||||
message_t *create_message( fish_message_type_t type,
|
||||
const wchar_t *key_in,
|
||||
const wchar_t *val_in )
|
||||
{
|
||||
message_t *msg = new message_t;
|
||||
msg->count = 0;
|
||||
|
||||
char *key=0;
|
||||
size_t sz;
|
||||
|
||||
// debug( 4, L"Crete message of type %d", type );
|
||||
|
||||
// debug( 4, L"Crete message of type %d", type );
|
||||
|
||||
if( key_in )
|
||||
{
|
||||
@@ -778,8 +837,8 @@ message_t *create_message( int type,
|
||||
if( !key )
|
||||
{
|
||||
debug( 0,
|
||||
L"Could not convert %ls to narrow character string",
|
||||
key_in );
|
||||
L"Could not convert %ls to narrow character string",
|
||||
key_in );
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -795,74 +854,42 @@ message_t *create_message( int type,
|
||||
val_in=L"";
|
||||
}
|
||||
|
||||
wcstring esc = full_escape( val_in );
|
||||
wcstring esc = full_escape( val_in );
|
||||
char *val = wcs2utf(esc.c_str());
|
||||
|
||||
sz = strlen(type==SET?SET_MBS:SET_EXPORT_MBS) + strlen(key) + strlen(val) + 4;
|
||||
msg = (message_t *)malloc( sizeof( message_t ) + sz );
|
||||
|
||||
if( !msg )
|
||||
DIE_MEM();
|
||||
|
||||
strcpy( msg->body, (type==SET?SET_MBS:SET_EXPORT_MBS) );
|
||||
strcat( msg->body, " " );
|
||||
strcat( msg->body, key );
|
||||
strcat( msg->body, ":" );
|
||||
strcat( msg->body, val );
|
||||
strcat( msg->body, "\n" );
|
||||
|
||||
set_body(msg, (type==SET?SET_MBS:SET_EXPORT_MBS), " ", key, ":", val, "\n", NULL);
|
||||
free( val );
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case ERASE:
|
||||
{
|
||||
sz = strlen(ERASE_MBS) + strlen(key) + 3;
|
||||
msg = (message_t *)malloc( sizeof( message_t ) + sz );
|
||||
|
||||
if( !msg )
|
||||
DIE_MEM();
|
||||
|
||||
strcpy( msg->body, ERASE_MBS " " );
|
||||
strcat( msg->body, key );
|
||||
strcat( msg->body, "\n" );
|
||||
set_body(msg, ERASE_MBS, " ", key, "\n", NULL);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case BARRIER:
|
||||
{
|
||||
msg = (message_t *)malloc( sizeof( message_t ) +
|
||||
strlen( BARRIER_MBS ) +2);
|
||||
if( !msg )
|
||||
DIE_MEM();
|
||||
strcpy( msg->body, BARRIER_MBS "\n" );
|
||||
set_body(msg, BARRIER_MBS, "\n", NULL);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case BARRIER_REPLY:
|
||||
{
|
||||
msg = (message_t *)malloc( sizeof( message_t ) +
|
||||
strlen( BARRIER_REPLY_MBS ) +2);
|
||||
if( !msg )
|
||||
DIE_MEM();
|
||||
strcpy( msg->body, BARRIER_REPLY_MBS "\n" );
|
||||
set_body(msg, BARRIER_REPLY_MBS, "\n", NULL);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
default:
|
||||
{
|
||||
debug( 0, L"create_message: Unknown message type" );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
free( key );
|
||||
|
||||
if( msg )
|
||||
msg->count=0;
|
||||
|
||||
// debug( 4, L"Message body is '%s'", msg->body );
|
||||
|
||||
|
||||
// debug( 4, L"Message body is '%s'", msg->body );
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
@@ -873,7 +900,7 @@ void env_universal_common_get_names( wcstring_list_t &lst,
|
||||
int show_exported,
|
||||
int show_unexported )
|
||||
{
|
||||
std::map<wcstring, var_uni_entry_t*>::const_iterator iter;
|
||||
env_var_table_t::const_iterator iter;
|
||||
for (iter = env_universal_var.begin(); iter != env_universal_var.end(); ++iter)
|
||||
{
|
||||
const wcstring& key = iter->first;
|
||||
@@ -891,7 +918,7 @@ void env_universal_common_get_names( wcstring_list_t &lst,
|
||||
|
||||
wchar_t *env_universal_common_get( const wcstring &name )
|
||||
{
|
||||
std::map<wcstring, var_uni_entry_t*>::const_iterator result = env_universal_var.find(name);
|
||||
env_var_table_t::const_iterator result = env_universal_var.find(name);
|
||||
|
||||
if (result != env_universal_var.end() )
|
||||
{
|
||||
@@ -905,7 +932,7 @@ wchar_t *env_universal_common_get( const wcstring &name )
|
||||
|
||||
int env_universal_common_get_export( const wcstring &name )
|
||||
{
|
||||
std::map<wcstring, var_uni_entry_t*>::const_iterator result = env_universal_var.find(name);
|
||||
env_var_table_t::const_iterator result = env_universal_var.find(name);
|
||||
if (result != env_universal_var.end() )
|
||||
{
|
||||
const var_uni_entry_t *e = result->second;
|
||||
@@ -917,7 +944,7 @@ int env_universal_common_get_export( const wcstring &name )
|
||||
|
||||
void enqueue_all( connection_t *c )
|
||||
{
|
||||
std::map<wcstring, var_uni_entry_t*>::const_iterator iter;
|
||||
env_var_table_t::const_iterator iter;
|
||||
|
||||
for (iter = env_universal_var.begin(); iter != env_universal_var.end(); ++iter)
|
||||
{
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <wchar.h>
|
||||
#include <queue>
|
||||
#include <string>
|
||||
#include "util.h"
|
||||
|
||||
/**
|
||||
@@ -39,15 +40,14 @@
|
||||
/**
|
||||
The different types of commands that can be sent between client/server
|
||||
*/
|
||||
enum
|
||||
typedef enum
|
||||
{
|
||||
SET,
|
||||
SET_EXPORT,
|
||||
ERASE,
|
||||
BARRIER,
|
||||
BARRIER_REPLY,
|
||||
}
|
||||
;
|
||||
} fish_message_type_t;
|
||||
|
||||
/**
|
||||
The size of the buffer used for reading from the socket
|
||||
@@ -63,12 +63,13 @@ typedef struct
|
||||
Number of queues that contain this message. Once this reaches zero, the message should be deleted
|
||||
*/
|
||||
int count;
|
||||
|
||||
/**
|
||||
Message body. The message must be allocated using enough memory to actually contain the message.
|
||||
*/
|
||||
char body[1];
|
||||
}
|
||||
message_t;
|
||||
std::string body;
|
||||
|
||||
} message_t;
|
||||
|
||||
typedef std::queue<message_t *> message_queue_t;
|
||||
|
||||
@@ -103,12 +104,12 @@ typedef struct connection
|
||||
/**
|
||||
Number of bytes that have already been consumed.
|
||||
*/
|
||||
int buffer_consumed;
|
||||
size_t buffer_consumed;
|
||||
|
||||
/**
|
||||
Number of bytes that have been read into the buffer.
|
||||
*/
|
||||
int buffer_used;
|
||||
size_t buffer_used;
|
||||
|
||||
|
||||
/**
|
||||
@@ -131,12 +132,12 @@ void try_send_all( connection_t *c );
|
||||
/**
|
||||
Create a messge with the specified properties
|
||||
*/
|
||||
message_t *create_message( int type, const wchar_t *key, const wchar_t *val );
|
||||
message_t *create_message( fish_message_type_t type, const wchar_t *key, const wchar_t *val );
|
||||
|
||||
/**
|
||||
Init the library
|
||||
*/
|
||||
void env_universal_common_init(void (*cb)(int type, const wchar_t *key, const wchar_t *val ) );
|
||||
void env_universal_common_init(void (*cb)(fish_message_type_t type, const wchar_t *key, const wchar_t *val ) );
|
||||
|
||||
/**
|
||||
Destroy library data
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#
|
||||
# Init file for fish
|
||||
#
|
||||
# @configure_input@
|
||||
|
||||
#
|
||||
# Some things should only be done for login terminals
|
||||
11
event.cpp
11
event.cpp
@@ -443,7 +443,7 @@ static void event_fire_internal( const event_t *event )
|
||||
}
|
||||
}
|
||||
|
||||
// debug( 1, L"Event handler fires command '%ls'", (wchar_t *)b->buff );
|
||||
// debug( 1, L"Event handler fires command '%ls'", buffer.c_str() );
|
||||
|
||||
/*
|
||||
Event handlers are not part of the main flow of code, so
|
||||
@@ -452,9 +452,10 @@ static void event_fire_internal( const event_t *event )
|
||||
proc_push_interactive(0);
|
||||
prev_status = proc_get_last_status();
|
||||
parser_t &parser = parser_t::principal_parser();
|
||||
parser.push_block( EVENT );
|
||||
parser.current_block->state1<const event_t *>() = event;
|
||||
parser.eval( buffer.c_str(), 0, TOP );
|
||||
|
||||
block_t *block = new event_block_t(event);
|
||||
parser.push_block(block);
|
||||
parser.eval( buffer, io_chain_t(), TOP );
|
||||
parser.pop_block();
|
||||
proc_pop_interactive();
|
||||
proc_set_last_status( prev_status );
|
||||
@@ -528,7 +529,7 @@ static void event_fire_delayed()
|
||||
/*
|
||||
Send all signals in our private list
|
||||
*/
|
||||
for( i=0; i<(size_t)lst->count; i++ )
|
||||
for( int i=0; i < lst->count; i++ )
|
||||
{
|
||||
e.param1.signal = lst->signal[i];
|
||||
e.arguments->at(0) = sig2wcs( e.param1.signal );
|
||||
|
||||
2
event.h
2
event.h
@@ -85,7 +85,7 @@ struct event_t
|
||||
*/
|
||||
std::auto_ptr<wcstring_list_t> arguments;
|
||||
|
||||
event_t(int t) : type(t), param1() { }
|
||||
event_t(int t) : type(t), param1(), str_param1(), function_name(), arguments() { }
|
||||
|
||||
/** Copy constructor */
|
||||
event_t(const event_t &x);
|
||||
|
||||
555
exec.cpp
555
exec.cpp
@@ -24,7 +24,6 @@
|
||||
#include <dirent.h>
|
||||
#include <time.h>
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
@@ -50,6 +49,7 @@
|
||||
#include "expand.h"
|
||||
#include "signal.h"
|
||||
|
||||
|
||||
#include "parse_util.h"
|
||||
|
||||
/**
|
||||
@@ -96,6 +96,8 @@ static void exec_write_and_exit( int fd, const char *buff, size_t count, int sta
|
||||
|
||||
void exec_close( int fd )
|
||||
{
|
||||
ASSERT_IS_MAIN_THREAD();
|
||||
|
||||
/* This may be called in a child of fork(), so don't allocate memory */
|
||||
if( fd < 0 )
|
||||
{
|
||||
@@ -114,7 +116,7 @@ void exec_close( int fd )
|
||||
}
|
||||
|
||||
/* Maybe remove this from our set of open fds */
|
||||
if (fd < (int)open_fds.size()) {
|
||||
if ((size_t)fd < open_fds.size()) {
|
||||
open_fds[fd] = false;
|
||||
}
|
||||
}
|
||||
@@ -123,7 +125,7 @@ int exec_pipe( int fd[2])
|
||||
{
|
||||
int res;
|
||||
|
||||
while( ( res=pipe( fd ) ) )
|
||||
while ((res=pipe(fd)))
|
||||
{
|
||||
if( errno != EINTR )
|
||||
{
|
||||
@@ -135,7 +137,7 @@ int exec_pipe( int fd[2])
|
||||
debug( 4, L"Created pipe using fds %d and %d", fd[0], fd[1]);
|
||||
|
||||
int max_fd = std::max(fd[0], fd[1]);
|
||||
if ((int)open_fds.size() <= max_fd) {
|
||||
if (max_fd >= 0 && open_fds.size() <= (size_t)max_fd) {
|
||||
open_fds.resize(max_fd + 1, false);
|
||||
}
|
||||
open_fds.at(fd[0]) = true;
|
||||
@@ -147,24 +149,25 @@ int exec_pipe( int fd[2])
|
||||
/**
|
||||
Check if the specified fd is used as a part of a pipeline in the
|
||||
specidied set of IO redirections.
|
||||
This is called after fork().
|
||||
|
||||
\param fd the fd to search for
|
||||
\param io the set of io redirections to search in
|
||||
\param io_chain the set of io redirections to search in
|
||||
*/
|
||||
static int use_fd_in_pipe( int fd, io_data_t *io )
|
||||
{
|
||||
if( !io )
|
||||
return 0;
|
||||
|
||||
if( ( io->io_mode == IO_BUFFER ) ||
|
||||
( io->io_mode == IO_PIPE ) )
|
||||
{
|
||||
if( io->param1.pipe_fd[0] == fd ||
|
||||
io->param1.pipe_fd[1] == fd )
|
||||
return 1;
|
||||
}
|
||||
|
||||
return use_fd_in_pipe( fd, io->next );
|
||||
static bool use_fd_in_pipe(int fd, const io_chain_t &io_chain )
|
||||
{
|
||||
for (size_t idx = 0; idx < io_chain.size(); idx++)
|
||||
{
|
||||
const io_data_t *io = io_chain.at(idx);
|
||||
if( ( io->io_mode == IO_BUFFER ) ||
|
||||
( io->io_mode == IO_PIPE ) )
|
||||
{
|
||||
if( io->param1.pipe_fd[0] == fd ||
|
||||
io->param1.pipe_fd[1] == fd )
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -176,13 +179,13 @@ static int use_fd_in_pipe( int fd, io_data_t *io )
|
||||
\param io the list of io redirections for this job. Pipes mentioned
|
||||
here should not be closed.
|
||||
*/
|
||||
void close_unused_internal_pipes( io_data_t *io )
|
||||
void close_unused_internal_pipes( const io_chain_t &io )
|
||||
{
|
||||
/* A call to exec_close will modify open_fds, so be careful how we walk */
|
||||
for (size_t i=0; i < open_fds.size(); i++) {
|
||||
if (open_fds[i]) {
|
||||
int fd = (int)i;
|
||||
if( !use_fd_in_pipe( fd, io) )
|
||||
if( !use_fd_in_pipe(fd, io))
|
||||
{
|
||||
debug( 4, L"Close fd %d, used in other context", fd );
|
||||
exec_close( fd );
|
||||
@@ -192,11 +195,24 @@ void close_unused_internal_pipes( io_data_t *io )
|
||||
}
|
||||
}
|
||||
|
||||
void get_unused_internal_pipes(std::vector<int> &fds, const io_chain_t &io)
|
||||
{
|
||||
for (size_t i=0; i < open_fds.size(); i++) {
|
||||
if (open_fds[i]) {
|
||||
int fd = (int)i;
|
||||
if( !use_fd_in_pipe(fd, io))
|
||||
{
|
||||
fds.push_back(fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the interpreter for the specified script. Returns NULL if file
|
||||
is not a script with a shebang.
|
||||
*/
|
||||
static char *get_interpreter( const char *command, char *interpreter, size_t buff_size )
|
||||
char *get_interpreter( const char *command, char *interpreter, size_t buff_size )
|
||||
{
|
||||
// OK to not use CLO_EXEC here because this is only called after fork
|
||||
int fd = open( command, O_RDONLY );
|
||||
@@ -238,9 +254,8 @@ static void safe_launch_process( process_t *p, const char *actual_cmd, char **ar
|
||||
|
||||
// debug( 1, L"exec '%ls'", p->argv[0] );
|
||||
|
||||
execve ( wcs2str(p->actual_cmd),
|
||||
argv,
|
||||
envv );
|
||||
// Wow, this wcs2str call totally allocates memory
|
||||
execve ( actual_cmd, argv, envv );
|
||||
|
||||
err = errno;
|
||||
|
||||
@@ -274,88 +289,8 @@ static void safe_launch_process( process_t *p, const char *actual_cmd, char **ar
|
||||
}
|
||||
|
||||
errno = err;
|
||||
debug_safe( 0, "Failed to execute process '%s'. Reason:", actual_cmd );
|
||||
|
||||
switch( errno )
|
||||
{
|
||||
|
||||
case E2BIG:
|
||||
{
|
||||
char sz1[128], sz2[128];
|
||||
|
||||
long arg_max = -1;
|
||||
|
||||
size_t sz = 0;
|
||||
char **p;
|
||||
for(p=argv; *p; p++)
|
||||
{
|
||||
sz += strlen(*p)+1;
|
||||
}
|
||||
|
||||
for(p=envv; *p; p++)
|
||||
{
|
||||
sz += strlen(*p)+1;
|
||||
}
|
||||
|
||||
format_size_safe(sz1, sz);
|
||||
arg_max = sysconf( _SC_ARG_MAX );
|
||||
|
||||
if( arg_max > 0 )
|
||||
{
|
||||
format_size_safe(sz2, sz);
|
||||
debug_safe(0, "The total size of the argument and environment lists %s exceeds the operating system limit of %s.", sz1, sz2);
|
||||
}
|
||||
else
|
||||
{
|
||||
debug_safe( 0, "The total size of the argument and environment lists (%s) exceeds the operating system limit.", sz1);
|
||||
}
|
||||
|
||||
debug_safe(0, "Try running the command again with fewer arguments.");
|
||||
exit_without_destructors(STATUS_EXEC_FAIL);
|
||||
break;
|
||||
}
|
||||
|
||||
case ENOEXEC:
|
||||
{
|
||||
/* Hope strerror doesn't allocate... */
|
||||
const char *err = strerror(errno);
|
||||
debug_safe(0, "exec: %s", err);
|
||||
|
||||
debug_safe(0, "The file '%ls' is marked as an executable but could not be run by the operating system.", actual_cmd);
|
||||
exit_without_destructors(STATUS_EXEC_FAIL);
|
||||
}
|
||||
|
||||
case ENOENT:
|
||||
{
|
||||
char interpreter_buff[128] = {}, *interpreter;
|
||||
interpreter = get_interpreter(actual_cmd, interpreter_buff, sizeof interpreter_buff);
|
||||
if( interpreter && 0 != access( interpreter, X_OK ) )
|
||||
{
|
||||
debug_safe(0, "The file '%s' specified the interpreter '%s', which is not an executable command.", actual_cmd, interpreter );
|
||||
}
|
||||
else
|
||||
{
|
||||
debug_safe(0, "The file '%s' or a script or ELF interpreter does not exist, or a shared library needed for file or interpreter cannot be found.", actual_cmd);
|
||||
}
|
||||
exit_without_destructors(STATUS_EXEC_FAIL);
|
||||
}
|
||||
|
||||
case ENOMEM:
|
||||
{
|
||||
debug_safe(0, "Out of memory");
|
||||
exit_without_destructors(STATUS_EXEC_FAIL);
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
/* Hope strerror doesn't allocate... */
|
||||
const char *err = strerror(errno);
|
||||
debug_safe(0, "exec: %s", err);
|
||||
|
||||
// debug(0, L"The file '%ls' is marked as an executable but could not be run by the operating system.", p->actual_cmd);
|
||||
exit_without_destructors(STATUS_EXEC_FAIL);
|
||||
}
|
||||
}
|
||||
safe_report_exec_error(errno, actual_cmd, argv, envv);
|
||||
exit_without_destructors(STATUS_EXEC_FAIL);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -367,8 +302,8 @@ static void launch_process_nofork( process_t *p )
|
||||
ASSERT_IS_NOT_FORKED_CHILD();
|
||||
|
||||
char **argv = wcsv2strv(p->get_argv());
|
||||
char **envv = env_export_arr( 0 );
|
||||
char *actual_cmd = wcs2str(p->actual_cmd);
|
||||
char **envv = env_export_arr( false );
|
||||
char *actual_cmd = wcs2str(p->actual_cmd.c_str());
|
||||
|
||||
/* Bounce to launch_process. This never returns. */
|
||||
safe_launch_process(p, actual_cmd, argv, envv);
|
||||
@@ -379,33 +314,26 @@ static void launch_process_nofork( process_t *p )
|
||||
Check if the IO redirection chains contains redirections for the
|
||||
specified file descriptor
|
||||
*/
|
||||
static int has_fd( io_data_t *d, int fd )
|
||||
static int has_fd( const io_chain_t &d, int fd )
|
||||
{
|
||||
return io_get( d, fd ) != 0;
|
||||
return io_chain_get( d, fd ) != NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Free a transmogrified io chain. Only the chain itself and resources
|
||||
used by a transmogrified IO_FILE redirection are freed, since the
|
||||
original chain may still be needed.
|
||||
*/
|
||||
static void io_untransmogrify( io_data_t * in, io_data_t *out )
|
||||
{
|
||||
if( !out )
|
||||
return;
|
||||
assert(in != NULL);
|
||||
io_untransmogrify( in->next, out->next );
|
||||
switch( in->io_mode )
|
||||
{
|
||||
case IO_FILE:
|
||||
exec_close( out->param1.old_fd );
|
||||
break;
|
||||
}
|
||||
delete out;
|
||||
static void io_cleanup_chains(io_chain_t &chains, const std::vector<int> &opened_fds) {
|
||||
/* Close all the fds */
|
||||
for (size_t idx = 0; idx < opened_fds.size(); idx++) {
|
||||
close(opened_fds.at(idx));
|
||||
}
|
||||
|
||||
/* Then delete all of the redirections we made */
|
||||
chains.destroy();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Make a copy of the specified io redirection chain, but change file
|
||||
redirection into fd redirection. This makes the redirection chain
|
||||
@@ -415,68 +343,99 @@ static void io_untransmogrify( io_data_t * in, io_data_t *out )
|
||||
|
||||
\return the transmogrified chain on sucess, or 0 on failiure
|
||||
*/
|
||||
static io_data_t *io_transmogrify( io_data_t * in )
|
||||
{
|
||||
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();
|
||||
if( !in )
|
||||
return 0;
|
||||
|
||||
std::auto_ptr<io_data_t> out(new io_data_t);
|
||||
|
||||
out->fd = in->fd;
|
||||
out->io_mode = IO_FD;
|
||||
out->param2.close_old = 1;
|
||||
out->next=0;
|
||||
|
||||
switch( in->io_mode )
|
||||
{
|
||||
/*
|
||||
These redirections don't need transmogrification. They can be passed through.
|
||||
*/
|
||||
case IO_FD:
|
||||
case IO_CLOSE:
|
||||
case IO_BUFFER:
|
||||
case IO_PIPE:
|
||||
{
|
||||
out.reset(new io_data_t(*in));
|
||||
break;
|
||||
}
|
||||
assert(out_chain.empty());
|
||||
|
||||
/* Just to be clear what we do for an empty chain */
|
||||
if (in_chain.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool success = true;
|
||||
|
||||
/* Make our chain of redirections */
|
||||
io_chain_t result_chain;
|
||||
|
||||
/* In the event we can't finish transmorgrifying, we'll have to close all the files we opened. */
|
||||
std::vector<int> opened_fds;
|
||||
|
||||
for (size_t idx = 0; idx < in_chain.size(); idx++)
|
||||
{
|
||||
io_data_t *in = in_chain.at(idx);
|
||||
io_data_t *out = NULL; //gets allocated via new
|
||||
|
||||
switch( in->io_mode )
|
||||
{
|
||||
default:
|
||||
/* Unknown type, should never happen */
|
||||
fprintf(stderr, "Unknown io_mode %ld\n", (long)in->io_mode);
|
||||
abort();
|
||||
break;
|
||||
|
||||
/*
|
||||
These redirections don't need transmogrification. They can be passed through.
|
||||
*/
|
||||
case IO_PIPE:
|
||||
case IO_FD:
|
||||
case IO_BUFFER:
|
||||
case IO_CLOSE:
|
||||
{
|
||||
out = new io_data_t(*in);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
Transmogrify file redirections
|
||||
*/
|
||||
case IO_FILE:
|
||||
{
|
||||
out = new io_data_t();
|
||||
out->fd = in->fd;
|
||||
out->io_mode = IO_FD;
|
||||
out->param2.close_old = 1;
|
||||
|
||||
/*
|
||||
Transmogrify file redirections
|
||||
*/
|
||||
case IO_FILE:
|
||||
{
|
||||
int fd;
|
||||
|
||||
if( (fd=open( in->filename_cstr, in->param2.flags, OPEN_MASK ) )==-1 )
|
||||
{
|
||||
debug( 1,
|
||||
FILE_ERROR,
|
||||
in->filename_cstr );
|
||||
|
||||
wperror( L"open" );
|
||||
return NULL;
|
||||
}
|
||||
int fd;
|
||||
if ((fd=open(in->filename_cstr, in->param2.flags, OPEN_MASK))==-1)
|
||||
{
|
||||
debug( 1,
|
||||
FILE_ERROR,
|
||||
in->filename_cstr );
|
||||
|
||||
wperror( L"open" );
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
|
||||
out->param1.old_fd = fd;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( in->next)
|
||||
{
|
||||
out->next = io_transmogrify( in->next );
|
||||
if( !out->next )
|
||||
{
|
||||
io_untransmogrify( in, out.release() );
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return out.release();
|
||||
opened_fds.push_back(fd);
|
||||
out->param1.old_fd = fd;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Record this IO redirection even if we failed (so we can free it) */
|
||||
result_chain.push_back(out);
|
||||
|
||||
/* But don't go any further if we failed */
|
||||
if (! success) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Now either return success, or clean up */
|
||||
if (success) {
|
||||
/* Yay */
|
||||
out_chain.swap(result_chain);
|
||||
out_opened_fds.swap(opened_fds);
|
||||
} else {
|
||||
/* No dice - clean up */
|
||||
io_cleanup_chains(result_chain, opened_fds);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Morph an io redirection chain into redirections suitable for
|
||||
passing to eval, call eval, and clean up morphed redirections.
|
||||
@@ -489,16 +448,19 @@ static io_data_t *io_transmogrify( io_data_t * in )
|
||||
static void internal_exec_helper( parser_t &parser,
|
||||
const wchar_t *def,
|
||||
enum block_type_t block_type,
|
||||
io_data_t *io )
|
||||
io_chain_t &ios )
|
||||
{
|
||||
io_data_t *io_internal = io_transmogrify( io );
|
||||
io_chain_t morphed_chain;
|
||||
std::vector<int> opened_fds;
|
||||
bool transmorgrified = io_transmogrify(ios, morphed_chain, opened_fds);
|
||||
|
||||
int is_block_old=is_block;
|
||||
is_block=1;
|
||||
|
||||
/*
|
||||
Did the transmogrification fail - if so, set error status and return
|
||||
*/
|
||||
if( io && !io_internal )
|
||||
if( ! transmorgrified )
|
||||
{
|
||||
proc_set_last_status( STATUS_EXEC_FAIL );
|
||||
return;
|
||||
@@ -506,11 +468,11 @@ static void internal_exec_helper( parser_t &parser,
|
||||
|
||||
signal_unblock();
|
||||
|
||||
parser.eval( def, io_internal, block_type );
|
||||
parser.eval( def, morphed_chain, block_type );
|
||||
|
||||
signal_block();
|
||||
|
||||
io_untransmogrify( io, io_internal );
|
||||
io_cleanup_chains(morphed_chain, opened_fds);
|
||||
job_reap( 0 );
|
||||
is_block=is_block_old;
|
||||
}
|
||||
@@ -542,24 +504,59 @@ static void do_builtin_io( const char *out, const char *err )
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns whether we can use posix spawn for a given process in a given job.
|
||||
Per https://github.com/fish-shell/fish-shell/issues/364 , error handling for file redirections is too difficult with posix_spawn
|
||||
So in that case we use fork/exec
|
||||
|
||||
Furthermore, to avoid the race between the caller calling tcsetpgrp() and the client checking the foreground process group, we don't use posix_spawn if we're going to foreground the process. (If we use fork(), we can call tcsetpgrp after the fork, before the exec, and avoid the racse).
|
||||
*/
|
||||
static bool can_use_posix_spawn_for_job(const job_t *job, const process_t *process)
|
||||
{
|
||||
if ( job_get_flag( job, JOB_CONTROL ) )
|
||||
{
|
||||
/* We are going to use job control; therefore when we launch this job it will get its own process group ID. But will it be foregrounded? */
|
||||
if ( job_get_flag( job, JOB_TERMINAL ) && job_get_flag( job, JOB_FOREGROUND ) )
|
||||
{
|
||||
/* It will be foregrounded, so we will call tcsetpgrp(), therefore do not use posix_spawn */
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool result = true;
|
||||
for (size_t idx = 0; idx < job->io.size(); idx++)
|
||||
{
|
||||
const io_data_t *io = job->io.at(idx);
|
||||
if (io->io_mode == IO_FILE)
|
||||
{
|
||||
const char *path = io->filename_cstr;
|
||||
/* This IO action is a file redirection. Only allow /dev/null, which is a common case we assume won't fail. */
|
||||
if (strcmp(path, "/dev/null") != 0)
|
||||
{
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void exec( parser_t &parser, job_t *j )
|
||||
{
|
||||
process_t *p;
|
||||
pid_t pid;
|
||||
pid_t pid = 0;
|
||||
int mypipe[2];
|
||||
sigset_t chldset;
|
||||
|
||||
io_data_t pipe_read, pipe_write;
|
||||
io_data_t *tmp;
|
||||
|
||||
io_data_t *io_buffer =0;
|
||||
|
||||
/*
|
||||
Set to 1 if something goes wrong while exec:ing the job, in
|
||||
Set to true if something goes wrong while exec:ing the job, in
|
||||
which case the cleanup code will kick in.
|
||||
*/
|
||||
int exec_error=0;
|
||||
bool exec_error = false;
|
||||
|
||||
bool needs_keepalive = false;
|
||||
process_t keepalive;
|
||||
@@ -576,23 +573,16 @@ void exec( parser_t &parser, job_t *j )
|
||||
|
||||
debug( 4, L"Exec job '%ls' with id %d", j->command_wcstr(), j->job_id );
|
||||
|
||||
if( parser.block_io )
|
||||
if( ! parser.block_io.empty() )
|
||||
{
|
||||
if( j->io )
|
||||
{
|
||||
j->io = io_add( io_duplicate(parser.block_io), j->io );
|
||||
}
|
||||
else
|
||||
{
|
||||
j->io=io_duplicate(parser.block_io);
|
||||
}
|
||||
io_duplicate_prepend(parser.block_io, j->io);
|
||||
}
|
||||
|
||||
|
||||
io_data_t *input_redirect;
|
||||
|
||||
for( input_redirect = j->io; input_redirect; input_redirect = input_redirect->next )
|
||||
const io_data_t *input_redirect = NULL;
|
||||
for (size_t idx = 0; idx < j->io.size(); idx++)
|
||||
{
|
||||
input_redirect = j->io.at(idx);
|
||||
|
||||
if( (input_redirect->io_mode == IO_BUFFER) &&
|
||||
input_redirect->is_input )
|
||||
{
|
||||
@@ -646,11 +636,9 @@ void exec( parser_t &parser, job_t *j )
|
||||
|
||||
pipe_write.io_mode=IO_PIPE;
|
||||
pipe_write.is_input = 0;
|
||||
pipe_read.next=0;
|
||||
pipe_write.next=0;
|
||||
pipe_write.param1.pipe_fd[0]=pipe_write.param1.pipe_fd[1]=-1;
|
||||
|
||||
j->io = io_add( j->io, &pipe_write );
|
||||
j->io.push_back(&pipe_write);
|
||||
|
||||
signal_block();
|
||||
|
||||
@@ -736,7 +724,7 @@ void exec( parser_t &parser, job_t *j )
|
||||
uniprocessor systems.
|
||||
*/
|
||||
if( p->type == EXTERNAL )
|
||||
env_export_arr( 1 );
|
||||
env_export_arr( true );
|
||||
|
||||
|
||||
/*
|
||||
@@ -745,7 +733,7 @@ void exec( parser_t &parser, job_t *j )
|
||||
|
||||
if( p == j->first_process->next )
|
||||
{
|
||||
j->io = io_add( j->io, &pipe_read );
|
||||
j->io.push_back(&pipe_read);
|
||||
}
|
||||
|
||||
if( p_wants_pipe )
|
||||
@@ -756,7 +744,7 @@ void exec( parser_t &parser, job_t *j )
|
||||
{
|
||||
debug( 1, PIPE_ERROR );
|
||||
wperror (L"pipe");
|
||||
exec_error=1;
|
||||
exec_error = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -768,8 +756,9 @@ void exec( parser_t &parser, job_t *j )
|
||||
This is the last element of the pipeline.
|
||||
Remove the io redirection for pipe output.
|
||||
*/
|
||||
j->io = io_remove( j->io, &pipe_write );
|
||||
|
||||
io_chain_t::iterator where = std::find(j->io.begin(), j->io.end(), &pipe_write);
|
||||
if (where != j->io.end())
|
||||
j->io.erase(where);
|
||||
}
|
||||
|
||||
switch( p->type )
|
||||
@@ -805,11 +794,9 @@ void exec( parser_t &parser, job_t *j )
|
||||
debug( 0, _( L"Unknown function '%ls'" ), p->argv0() );
|
||||
break;
|
||||
}
|
||||
parser.push_block( shadows?FUNCTION_CALL:FUNCTION_CALL_NO_SHADOW );
|
||||
|
||||
parser.current_block->state2<process_t *>() = p;
|
||||
parser.current_block->state1<wcstring>() = p->argv0();
|
||||
|
||||
function_block_t *newv = new function_block_t(p, p->argv0(), shadows);
|
||||
parser.push_block( newv );
|
||||
|
||||
|
||||
/*
|
||||
set_argv might trigger an event
|
||||
@@ -824,8 +811,8 @@ void exec( parser_t &parser, job_t *j )
|
||||
|
||||
if( p->next )
|
||||
{
|
||||
io_buffer = io_buffer_create( 0 );
|
||||
j->io = io_add( j->io, io_buffer );
|
||||
io_buffer = io_buffer_create( 0 );
|
||||
j->io.push_back(io_buffer);
|
||||
}
|
||||
|
||||
internal_exec_helper( parser, def, TOP, j->io );
|
||||
@@ -841,8 +828,8 @@ void exec( parser_t &parser, job_t *j )
|
||||
{
|
||||
if( p->next )
|
||||
{
|
||||
io_buffer = io_buffer_create( 0 );
|
||||
j->io = io_add( j->io, io_buffer );
|
||||
io_buffer = io_buffer_create( 0 );
|
||||
j->io.push_back(io_buffer);
|
||||
}
|
||||
|
||||
internal_exec_helper( parser, p->argv0(), TOP, j->io );
|
||||
@@ -863,7 +850,7 @@ void exec( parser_t &parser, job_t *j )
|
||||
*/
|
||||
if( p == j->first_process )
|
||||
{
|
||||
io_data_t *in = io_get( j->io, 0 );
|
||||
const io_data_t *in = io_chain_get( j->io, 0 );
|
||||
|
||||
if( in )
|
||||
{
|
||||
@@ -946,7 +933,7 @@ void exec( parser_t &parser, job_t *j )
|
||||
|
||||
if( builtin_stdin == -1 )
|
||||
{
|
||||
exec_error=1;
|
||||
exec_error = true;
|
||||
break;
|
||||
}
|
||||
else
|
||||
@@ -1039,7 +1026,7 @@ void exec( parser_t &parser, job_t *j )
|
||||
break;
|
||||
}
|
||||
|
||||
j->io = io_remove( j->io, io_buffer );
|
||||
io_remove( j->io, io_buffer );
|
||||
|
||||
io_buffer_read( io_buffer );
|
||||
|
||||
@@ -1158,7 +1145,7 @@ void exec( parser_t &parser, job_t *j )
|
||||
performance quite a bit in complex completion code.
|
||||
*/
|
||||
|
||||
io_data_t *io = io_get( j->io, 1 );
|
||||
io_data_t *io = io_chain_get( j->io, 1 );
|
||||
bool buffer_stdout = io && io->io_mode == IO_BUFFER;
|
||||
|
||||
if( ( get_stderr_buffer().empty() ) &&
|
||||
@@ -1166,15 +1153,15 @@ void exec( parser_t &parser, job_t *j )
|
||||
( ! get_stdout_buffer().empty() ) &&
|
||||
( buffer_stdout ) )
|
||||
{
|
||||
std::string res = wcs2string( get_stdout_buffer() );
|
||||
const std::string res = wcs2string( get_stdout_buffer() );
|
||||
io->out_buffer_append( res.c_str(), res.size() );
|
||||
skip_fork = 1;
|
||||
}
|
||||
|
||||
if (! skip_fork && ! j->io) {
|
||||
if (! skip_fork && j->io.empty()) {
|
||||
/* PCA for some reason, fish forks a lot, even for basic builtins like echo just to write out their buffers. I'm certain a lot of this is unnecessary, but I am not sure exactly when. If j->io is NULL, then it means there's no pipes or anything, so we can certainly just write out our data. Beyond that, we may be able to do the same if io_get returns 0 for STDOUT_FILENO and STDERR_FILENO. */
|
||||
if (g_log_forks) {
|
||||
printf("fork #-: Skipping fork for internal builtin for '%ls' (io is %p, job_io is %p)\n", p->argv0(), io, j->io);
|
||||
printf("fork #-: Skipping fork for internal builtin for '%ls'\n", p->argv0());
|
||||
}
|
||||
const wcstring &out = get_stdout_buffer(), &err = get_stderr_buffer();
|
||||
char *outbuff = wcs2str(out.c_str()), *errbuff = wcs2str(err.c_str());
|
||||
@@ -1184,8 +1171,9 @@ void exec( parser_t &parser, job_t *j )
|
||||
skip_fork = 1;
|
||||
}
|
||||
|
||||
for( io_data_t *tmp_io = j->io; tmp_io != NULL; tmp_io=tmp_io->next )
|
||||
for( io_chain_t::iterator iter = j->io.begin(); iter != j->io.end(); iter++ )
|
||||
{
|
||||
io_data_t *tmp_io = *iter;
|
||||
if( tmp_io->io_mode == IO_FILE && strcmp(tmp_io->filename_cstr, "/dev/null") != 0)
|
||||
{
|
||||
skip_fork = 0;
|
||||
@@ -1217,8 +1205,8 @@ void exec( parser_t &parser, job_t *j )
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
if (g_log_forks) {
|
||||
printf("fork #%d: Executing fork for internal builtin for '%ls' (io is %p, job_io is %p)\n", g_fork_count, p->argv0(), io, j->io);
|
||||
io_print(io);
|
||||
printf("fork #%d: Executing fork for internal builtin for '%ls'\n", g_fork_count, p->argv0());
|
||||
io_print(io_chain_t(io));
|
||||
}
|
||||
pid = execute_fork(false);
|
||||
if( pid == 0 )
|
||||
@@ -1273,33 +1261,74 @@ void exec( parser_t &parser, job_t *j )
|
||||
const wchar_t *file = reader_current_filename();
|
||||
const wchar_t *func = parser_t::principal_parser().is_function();
|
||||
printf("fork #%d: forking for '%s' in '%ls:%ls'\n", g_fork_count, actual_cmd, file ? file : L"", func ? func : L"?");
|
||||
|
||||
fprintf(stderr, "IO chain for %s:\n", actual_cmd);
|
||||
io_print(j->io);
|
||||
}
|
||||
|
||||
#if FISH_USE_POSIX_SPAWN
|
||||
/* Prefer to use posix_spawn, since it's faster on some systems like OS X */
|
||||
bool use_posix_spawn = g_use_posix_spawn && can_use_posix_spawn_for_job(j, p);
|
||||
if (use_posix_spawn)
|
||||
{
|
||||
/* Create posix spawn attributes and actions */
|
||||
posix_spawnattr_t attr = posix_spawnattr_t();
|
||||
posix_spawn_file_actions_t actions = posix_spawn_file_actions_t();
|
||||
bool made_it = fork_actions_make_spawn_properties(&attr, &actions, j, p);
|
||||
if (made_it)
|
||||
{
|
||||
/* We successfully made the attributes and actions; actually call posix_spawn */
|
||||
int spawn_ret = posix_spawn(&pid, actual_cmd, &actions, &attr, argv, envv);
|
||||
|
||||
/* This usleep can be used to test for various race conditions (https://github.com/fish-shell/fish-shell/issues/360) */
|
||||
//usleep(10000);
|
||||
|
||||
if (spawn_ret != 0)
|
||||
{
|
||||
safe_report_exec_error(spawn_ret, actual_cmd, argv, envv);
|
||||
/* Make sure our pid isn't set */
|
||||
pid = 0;
|
||||
}
|
||||
|
||||
/* Clean up our actions */
|
||||
posix_spawn_file_actions_destroy(&actions);
|
||||
posix_spawnattr_destroy(&attr);
|
||||
}
|
||||
|
||||
/* A 0 pid means we failed to posix_spawn. Since we have no pid, we'll never get told when it's exited, so we have to mark the process as failed. */
|
||||
if (pid == 0)
|
||||
{
|
||||
job_mark_process_as_failed(j, p);
|
||||
exec_error = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
pid = execute_fork(false);
|
||||
if (pid == 0)
|
||||
{
|
||||
/* This is the child process. */
|
||||
p->pid = getpid();
|
||||
setup_child_process( j, p );
|
||||
safe_launch_process( p, actual_cmd, argv, envv );
|
||||
|
||||
/*
|
||||
safe_launch_process _never_ returns...
|
||||
*/
|
||||
}
|
||||
}
|
||||
pid = execute_fork(false);
|
||||
if( pid == 0 )
|
||||
{
|
||||
/*
|
||||
This is the child process.
|
||||
*/
|
||||
p->pid = getpid();
|
||||
setup_child_process( j, p );
|
||||
safe_launch_process( p, actual_cmd, argv, envv );
|
||||
|
||||
/*
|
||||
safe_launch_process _never_ returns...
|
||||
*/
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
This is the parent process. Store away
|
||||
information on the child, and possibly fice
|
||||
it control over the terminal.
|
||||
*/
|
||||
p->pid = pid;
|
||||
|
||||
set_child_group( j, p, 0 );
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
This is the parent process. Store away
|
||||
information on the child, and possibly fice
|
||||
it control over the terminal.
|
||||
*/
|
||||
p->pid = pid;
|
||||
|
||||
set_child_group( j, p, 0 );
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1347,10 +1376,12 @@ void exec( parser_t &parser, job_t *j )
|
||||
|
||||
debug( 3, L"Job is constructed" );
|
||||
|
||||
j->io = io_remove( j->io, &pipe_read );
|
||||
|
||||
for( tmp = parser.block_io; tmp; tmp=tmp->next )
|
||||
j->io = io_remove( j->io, tmp );
|
||||
io_remove( j->io, &pipe_read );
|
||||
|
||||
for (io_chain_t::const_iterator iter = parser.block_io.begin(); iter != parser.block_io.end(); iter++)
|
||||
{
|
||||
io_remove( j->io, *iter );
|
||||
}
|
||||
|
||||
job_set_flag( j, JOB_CONSTRUCTED, 1 );
|
||||
|
||||
@@ -1399,7 +1430,7 @@ static int exec_subshell_internal( const wcstring &cmd, wcstring_list_t *lst )
|
||||
prev_status = proc_get_last_status();
|
||||
|
||||
parser_t &parser = parser_t::principal_parser();
|
||||
if( parser.eval( cmd, io_buffer, SUBST ) )
|
||||
if( parser.eval( cmd, io_chain_t(io_buffer), SUBST ) )
|
||||
{
|
||||
status = -1;
|
||||
}
|
||||
|
||||
8
exec.h
8
exec.h
@@ -71,6 +71,12 @@ void exec_close( int fd );
|
||||
int exec_pipe( int fd[2]);
|
||||
|
||||
/* Close all fds in open_fds. This is called from postfork.cpp */
|
||||
void close_unused_internal_pipes( io_data_t *io );
|
||||
void close_unused_internal_pipes( const io_chain_t &io );
|
||||
|
||||
/* Gets all unused internal pipes into fds */
|
||||
void get_unused_internal_pipes(std::vector<int> &fds, const io_chain_t &io);
|
||||
|
||||
/** Gets the interpreter for a given command */
|
||||
char *get_interpreter( const char *command, char *interpreter, size_t buff_size );
|
||||
|
||||
#endif
|
||||
|
||||
944
expand.cpp
944
expand.cpp
File diff suppressed because it is too large
Load Diff
15
expand.h
15
expand.h
@@ -46,7 +46,13 @@ enum {
|
||||
DIRECTORIES_ONLY = 1 << 5,
|
||||
|
||||
/** Don't generate descriptions */
|
||||
EXPAND_NO_DESCRIPTIONS = 1 << 6
|
||||
EXPAND_NO_DESCRIPTIONS = 1 << 6,
|
||||
|
||||
/** Don't do process expansion */
|
||||
EXPAND_SKIP_PROCESS = 1 << 7,
|
||||
|
||||
/** Don't expand jobs (but you can still expand processes). This is because job expansion is not thread safe. */
|
||||
EXPAND_SKIP_JOBS = 1 << 8
|
||||
};
|
||||
typedef int expand_flags_t;
|
||||
|
||||
@@ -163,7 +169,6 @@ wcstring expand_escape_variable( const wcstring &in );
|
||||
*/
|
||||
void expand_tilde(wcstring &input);
|
||||
|
||||
|
||||
/**
|
||||
Test if the specified argument is clean, i.e. it does not contain
|
||||
any tokens which need to be expanded or otherwise altered. Clean
|
||||
@@ -187,7 +192,11 @@ int expand_is_clean( const wchar_t *in );
|
||||
\param token_pos The position where the expansion begins
|
||||
\param error_pos The position on the line to report to the error function.
|
||||
*/
|
||||
void expand_variable_error( parser_t &parser, const wchar_t *token, int token_pos, int error_pos );
|
||||
void expand_variable_error( parser_t &parser, const wchar_t *token, size_t token_pos, int error_pos );
|
||||
|
||||
/**
|
||||
Testing function for getting all process names.
|
||||
*/
|
||||
std::vector<wcstring> expand_get_all_process_names(void);
|
||||
|
||||
#endif
|
||||
|
||||
330
fallback.cpp
330
fallback.cpp
@@ -74,7 +74,7 @@ int tputs(const char *str, int affcnt, int (*fish_putc)(tputs_arg_t))
|
||||
*/
|
||||
char *tparm_solaris_kludge( char *str, ... )
|
||||
{
|
||||
long int param[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
long int param[9] = { };
|
||||
|
||||
va_list ap;
|
||||
va_start( ap, str );
|
||||
@@ -803,8 +803,9 @@ wchar_t *wcstok(wchar_t *wcs, const wchar_t *delim, wchar_t **save_ptr)
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_WCSDUP
|
||||
wchar_t *wcsdup( const wchar_t *in )
|
||||
/* Fallback implementations of wcsdup and wcscasecmp. On systems where these are not needed (e.g. building on Linux) these should end up just being stripped, as they are static functions that are not referenced in this file.
|
||||
*/
|
||||
static wchar_t *wcsdup_fallback(const wchar_t *in)
|
||||
{
|
||||
size_t len=wcslen(in);
|
||||
wchar_t *out = (wchar_t *)malloc( sizeof( wchar_t)*(len+1));
|
||||
@@ -815,23 +816,9 @@ wchar_t *wcsdup( const wchar_t *in )
|
||||
|
||||
memcpy( out, in, sizeof( wchar_t)*(len+1));
|
||||
return out;
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_WCSLEN
|
||||
size_t wcslen(const wchar_t *in)
|
||||
{
|
||||
const wchar_t *end=in;
|
||||
while( *end )
|
||||
end++;
|
||||
return end-in;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef HAVE_WCSCASECMP
|
||||
int wcscasecmp( const wchar_t *a, const wchar_t *b )
|
||||
int wcscasecmp_fallback( const wchar_t *a, const wchar_t *b )
|
||||
{
|
||||
if( *a == 0 )
|
||||
{
|
||||
@@ -845,11 +832,55 @@ int wcscasecmp( const wchar_t *a, const wchar_t *b )
|
||||
if( diff != 0 )
|
||||
return diff;
|
||||
else
|
||||
return wcscasecmp( a+1,b+1);
|
||||
return wcscasecmp_fallback( a+1,b+1);
|
||||
}
|
||||
|
||||
|
||||
#if __APPLE__ && __DARWIN_C_LEVEL >= 200809L
|
||||
/* Note parens avoid the macro expansion */
|
||||
wchar_t *wcsdup_use_weak(const wchar_t *a)
|
||||
{
|
||||
if (wcsdup != NULL)
|
||||
return (wcsdup)(a);
|
||||
return wcsdup_fallback(a);
|
||||
}
|
||||
|
||||
int wcscasecmp_use_weak(const wchar_t *a, const wchar_t *b)
|
||||
{
|
||||
if (wcscasecmp != NULL)
|
||||
return (wcscasecmp)(a, b);
|
||||
return wcscasecmp_fallback(a, b);
|
||||
}
|
||||
|
||||
#else //__APPLE__
|
||||
|
||||
#ifndef HAVE_WCSDUP
|
||||
wchar_t *wcsdup( const wchar_t *in )
|
||||
{
|
||||
return wcsdup_fallback(in);
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef HAVE_WCSCASECMP
|
||||
int wcscasecmp( const wchar_t *a, const wchar_t *b )
|
||||
{
|
||||
return wcscasecmp_fallback(a, b);
|
||||
}
|
||||
#endif
|
||||
#endif //__APPLE__
|
||||
|
||||
#ifndef HAVE_WCSLEN
|
||||
size_t wcslen(const wchar_t *in)
|
||||
{
|
||||
const wchar_t *end=in;
|
||||
while( *end )
|
||||
end++;
|
||||
return end-in;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef HAVE_WCSNCASECMP
|
||||
int wcsncasecmp( const wchar_t *a, const wchar_t *b, int count )
|
||||
{
|
||||
@@ -884,7 +915,7 @@ int wcwidth( wchar_t c )
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_WCSNDUP
|
||||
wchar_t *wcsndup( const wchar_t *in, int c )
|
||||
wchar_t *wcsndup( const wchar_t *in, size_t c )
|
||||
{
|
||||
wchar_t *res = (wchar_t *)malloc( sizeof(wchar_t)*(c+1) );
|
||||
if( res == 0 )
|
||||
@@ -1080,7 +1111,7 @@ int lrand48_r(struct drand48_data *buffer, long int *result)
|
||||
|
||||
int srand48_r(long int seedval, struct drand48_data *buffer)
|
||||
{
|
||||
buffer->seed = (int)seedval;
|
||||
buffer->seed = (unsigned int)seedval;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1192,3 +1223,258 @@ double nan(char *tagp)
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Big hack to use our versions of wcswidth where we know them to be broken, like on OS X */
|
||||
#ifndef HAVE_BROKEN_WCWIDTH
|
||||
#if __APPLE__
|
||||
#define HAVE_BROKEN_WCWIDTH 1
|
||||
#else
|
||||
#define HAVE_BROKEN_WCWIDTH 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if ! HAVE_BROKEN_WCWIDTH
|
||||
|
||||
int fish_wcwidth(wchar_t wc)
|
||||
{
|
||||
return wcwidth(wc);
|
||||
}
|
||||
|
||||
int fish_wcswidth(const wchar_t *str, size_t n)
|
||||
{
|
||||
return wcswidth(str, n);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static int mk_wcwidth(wchar_t wc);
|
||||
static int mk_wcswidth(const wchar_t *pwcs, size_t n);
|
||||
|
||||
int fish_wcwidth(wchar_t wc)
|
||||
{
|
||||
return mk_wcwidth(wc);
|
||||
}
|
||||
|
||||
int fish_wcswidth(const wchar_t *str, size_t n)
|
||||
{
|
||||
return mk_wcswidth(str, n);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is an implementation of wcwidth() and wcswidth() (defined in
|
||||
* IEEE Std 1002.1-2001) for Unicode.
|
||||
*
|
||||
* http://www.opengroup.org/onlinepubs/007904975/functions/wcwidth.html
|
||||
* http://www.opengroup.org/onlinepubs/007904975/functions/wcswidth.html
|
||||
*
|
||||
* In fixed-width output devices, Latin characters all occupy a single
|
||||
* "cell" position of equal width, whereas ideographic CJK characters
|
||||
* occupy two such cells. Interoperability between terminal-line
|
||||
* applications and (teletype-style) character terminals using the
|
||||
* UTF-8 encoding requires agreement on which character should advance
|
||||
* the cursor by how many cell positions. No established formal
|
||||
* standards exist at present on which Unicode character shall occupy
|
||||
* how many cell positions on character terminals. These routines are
|
||||
* a first attempt of defining such behavior based on simple rules
|
||||
* applied to data provided by the Unicode Consortium.
|
||||
*
|
||||
* For some graphical characters, the Unicode standard explicitly
|
||||
* defines a character-cell width via the definition of the East Asian
|
||||
* FullWidth (F), Wide (W), Half-width (H), and Narrow (Na) classes.
|
||||
* In all these cases, there is no ambiguity about which width a
|
||||
* terminal shall use. For characters in the East Asian Ambiguous (A)
|
||||
* class, the width choice depends purely on a preference of backward
|
||||
* compatibility with either historic CJK or Western practice.
|
||||
* Choosing single-width for these characters is easy to justify as
|
||||
* the appropriate long-term solution, as the CJK practice of
|
||||
* displaying these characters as double-width comes from historic
|
||||
* implementation simplicity (8-bit encoded characters were displayed
|
||||
* single-width and 16-bit ones double-width, even for Greek,
|
||||
* Cyrillic, etc.) and not any typographic considerations.
|
||||
*
|
||||
* Much less clear is the choice of width for the Not East Asian
|
||||
* (Neutral) class. Existing practice does not dictate a width for any
|
||||
* of these characters. It would nevertheless make sense
|
||||
* typographically to allocate two character cells to characters such
|
||||
* as for instance EM SPACE or VOLUME INTEGRAL, which cannot be
|
||||
* represented adequately with a single-width glyph. The following
|
||||
* routines at present merely assign a single-cell width to all
|
||||
* neutral characters, in the interest of simplicity. This is not
|
||||
* entirely satisfactory and should be reconsidered before
|
||||
* establishing a formal standard in this area. At the moment, the
|
||||
* decision which Not East Asian (Neutral) characters should be
|
||||
* represented by double-width glyphs cannot yet be answered by
|
||||
* applying a simple rule from the Unicode database content. Setting
|
||||
* up a proper standard for the behavior of UTF-8 character terminals
|
||||
* will require a careful analysis not only of each Unicode character,
|
||||
* but also of each presentation form, something the author of these
|
||||
* routines has avoided to do so far.
|
||||
*
|
||||
* http://www.unicode.org/unicode/reports/tr11/
|
||||
*
|
||||
* Markus Kuhn -- 2007-05-26 (Unicode 5.0)
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software
|
||||
* for any purpose and without fee is hereby granted. The author
|
||||
* disclaims all warranties with regard to this software.
|
||||
*
|
||||
* Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
|
||||
*/
|
||||
|
||||
#include <wchar.h>
|
||||
|
||||
struct interval {
|
||||
int first;
|
||||
int last;
|
||||
};
|
||||
|
||||
/* auxiliary function for binary search in interval table */
|
||||
static int bisearch(wchar_t ucs, const struct interval *table, int max) {
|
||||
int min = 0;
|
||||
int mid;
|
||||
|
||||
if (ucs < table[0].first || ucs > table[max].last)
|
||||
return 0;
|
||||
while (max >= min) {
|
||||
mid = (min + max) / 2;
|
||||
if (ucs > table[mid].last)
|
||||
min = mid + 1;
|
||||
else if (ucs < table[mid].first)
|
||||
max = mid - 1;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* The following two functions define the column width of an ISO 10646
|
||||
* character as follows:
|
||||
*
|
||||
* - The null character (U+0000) has a column width of 0.
|
||||
*
|
||||
* - Other C0/C1 control characters and DEL will lead to a return
|
||||
* value of -1.
|
||||
*
|
||||
* - Non-spacing and enclosing combining characters (general
|
||||
* category code Mn or Me in the Unicode database) have a
|
||||
* column width of 0.
|
||||
*
|
||||
* - SOFT HYPHEN (U+00AD) has a column width of 1.
|
||||
*
|
||||
* - Other format characters (general category code Cf in the Unicode
|
||||
* database) and ZERO WIDTH SPACE (U+200B) have a column width of 0.
|
||||
*
|
||||
* - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF)
|
||||
* have a column width of 0.
|
||||
*
|
||||
* - Spacing characters in the East Asian Wide (W) or East Asian
|
||||
* Full-width (F) category as defined in Unicode Technical
|
||||
* Report #11 have a column width of 2.
|
||||
*
|
||||
* - All remaining characters (including all printable
|
||||
* ISO 8859-1 and WGL4 characters, Unicode control characters,
|
||||
* etc.) have a column width of 1.
|
||||
*
|
||||
* This implementation assumes that wchar_t characters are encoded
|
||||
* in ISO 10646.
|
||||
*/
|
||||
|
||||
static int mk_wcwidth(wchar_t ucs)
|
||||
{
|
||||
/* sorted list of non-overlapping intervals of non-spacing characters */
|
||||
/* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */
|
||||
static const struct interval combining[] = {
|
||||
{ 0x0300, 0x036F }, { 0x0483, 0x0486 }, { 0x0488, 0x0489 },
|
||||
{ 0x0591, 0x05BD }, { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 },
|
||||
{ 0x05C4, 0x05C5 }, { 0x05C7, 0x05C7 }, { 0x0600, 0x0603 },
|
||||
{ 0x0610, 0x0615 }, { 0x064B, 0x065E }, { 0x0670, 0x0670 },
|
||||
{ 0x06D6, 0x06E4 }, { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED },
|
||||
{ 0x070F, 0x070F }, { 0x0711, 0x0711 }, { 0x0730, 0x074A },
|
||||
{ 0x07A6, 0x07B0 }, { 0x07EB, 0x07F3 }, { 0x0901, 0x0902 },
|
||||
{ 0x093C, 0x093C }, { 0x0941, 0x0948 }, { 0x094D, 0x094D },
|
||||
{ 0x0951, 0x0954 }, { 0x0962, 0x0963 }, { 0x0981, 0x0981 },
|
||||
{ 0x09BC, 0x09BC }, { 0x09C1, 0x09C4 }, { 0x09CD, 0x09CD },
|
||||
{ 0x09E2, 0x09E3 }, { 0x0A01, 0x0A02 }, { 0x0A3C, 0x0A3C },
|
||||
{ 0x0A41, 0x0A42 }, { 0x0A47, 0x0A48 }, { 0x0A4B, 0x0A4D },
|
||||
{ 0x0A70, 0x0A71 }, { 0x0A81, 0x0A82 }, { 0x0ABC, 0x0ABC },
|
||||
{ 0x0AC1, 0x0AC5 }, { 0x0AC7, 0x0AC8 }, { 0x0ACD, 0x0ACD },
|
||||
{ 0x0AE2, 0x0AE3 }, { 0x0B01, 0x0B01 }, { 0x0B3C, 0x0B3C },
|
||||
{ 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B43 }, { 0x0B4D, 0x0B4D },
|
||||
{ 0x0B56, 0x0B56 }, { 0x0B82, 0x0B82 }, { 0x0BC0, 0x0BC0 },
|
||||
{ 0x0BCD, 0x0BCD }, { 0x0C3E, 0x0C40 }, { 0x0C46, 0x0C48 },
|
||||
{ 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 }, { 0x0CBC, 0x0CBC },
|
||||
{ 0x0CBF, 0x0CBF }, { 0x0CC6, 0x0CC6 }, { 0x0CCC, 0x0CCD },
|
||||
{ 0x0CE2, 0x0CE3 }, { 0x0D41, 0x0D43 }, { 0x0D4D, 0x0D4D },
|
||||
{ 0x0DCA, 0x0DCA }, { 0x0DD2, 0x0DD4 }, { 0x0DD6, 0x0DD6 },
|
||||
{ 0x0E31, 0x0E31 }, { 0x0E34, 0x0E3A }, { 0x0E47, 0x0E4E },
|
||||
{ 0x0EB1, 0x0EB1 }, { 0x0EB4, 0x0EB9 }, { 0x0EBB, 0x0EBC },
|
||||
{ 0x0EC8, 0x0ECD }, { 0x0F18, 0x0F19 }, { 0x0F35, 0x0F35 },
|
||||
{ 0x0F37, 0x0F37 }, { 0x0F39, 0x0F39 }, { 0x0F71, 0x0F7E },
|
||||
{ 0x0F80, 0x0F84 }, { 0x0F86, 0x0F87 }, { 0x0F90, 0x0F97 },
|
||||
{ 0x0F99, 0x0FBC }, { 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 },
|
||||
{ 0x1032, 0x1032 }, { 0x1036, 0x1037 }, { 0x1039, 0x1039 },
|
||||
{ 0x1058, 0x1059 }, { 0x1160, 0x11FF }, { 0x135F, 0x135F },
|
||||
{ 0x1712, 0x1714 }, { 0x1732, 0x1734 }, { 0x1752, 0x1753 },
|
||||
{ 0x1772, 0x1773 }, { 0x17B4, 0x17B5 }, { 0x17B7, 0x17BD },
|
||||
{ 0x17C6, 0x17C6 }, { 0x17C9, 0x17D3 }, { 0x17DD, 0x17DD },
|
||||
{ 0x180B, 0x180D }, { 0x18A9, 0x18A9 }, { 0x1920, 0x1922 },
|
||||
{ 0x1927, 0x1928 }, { 0x1932, 0x1932 }, { 0x1939, 0x193B },
|
||||
{ 0x1A17, 0x1A18 }, { 0x1B00, 0x1B03 }, { 0x1B34, 0x1B34 },
|
||||
{ 0x1B36, 0x1B3A }, { 0x1B3C, 0x1B3C }, { 0x1B42, 0x1B42 },
|
||||
{ 0x1B6B, 0x1B73 }, { 0x1DC0, 0x1DCA }, { 0x1DFE, 0x1DFF },
|
||||
{ 0x200B, 0x200F }, { 0x202A, 0x202E }, { 0x2060, 0x2063 },
|
||||
{ 0x206A, 0x206F }, { 0x20D0, 0x20EF }, { 0x302A, 0x302F },
|
||||
{ 0x3099, 0x309A }, { 0xA806, 0xA806 }, { 0xA80B, 0xA80B },
|
||||
{ 0xA825, 0xA826 }, { 0xFB1E, 0xFB1E }, { 0xFE00, 0xFE0F },
|
||||
{ 0xFE20, 0xFE23 }, { 0xFEFF, 0xFEFF }, { 0xFFF9, 0xFFFB },
|
||||
{ 0x10A01, 0x10A03 }, { 0x10A05, 0x10A06 }, { 0x10A0C, 0x10A0F },
|
||||
{ 0x10A38, 0x10A3A }, { 0x10A3F, 0x10A3F }, { 0x1D167, 0x1D169 },
|
||||
{ 0x1D173, 0x1D182 }, { 0x1D185, 0x1D18B }, { 0x1D1AA, 0x1D1AD },
|
||||
{ 0x1D242, 0x1D244 }, { 0xE0001, 0xE0001 }, { 0xE0020, 0xE007F },
|
||||
{ 0xE0100, 0xE01EF }
|
||||
};
|
||||
|
||||
/* test for 8-bit control characters */
|
||||
if (ucs == 0)
|
||||
return 0;
|
||||
if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0))
|
||||
return -1;
|
||||
|
||||
/* binary search in table of non-spacing characters */
|
||||
if (bisearch(ucs, combining,
|
||||
sizeof(combining) / sizeof(struct interval) - 1))
|
||||
return 0;
|
||||
|
||||
/* if we arrive here, ucs is not a combining or C0/C1 control character */
|
||||
|
||||
return 1 +
|
||||
(ucs >= 0x1100 &&
|
||||
(ucs <= 0x115f || /* Hangul Jamo init. consonants */
|
||||
ucs == 0x2329 || ucs == 0x232a ||
|
||||
(ucs >= 0x2e80 && ucs <= 0xa4cf &&
|
||||
ucs != 0x303f) || /* CJK ... Yi */
|
||||
(ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */
|
||||
(ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility Ideographs */
|
||||
(ucs >= 0xfe10 && ucs <= 0xfe19) || /* Vertical forms */
|
||||
(ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */
|
||||
(ucs >= 0xff00 && ucs <= 0xff60) || /* Fullwidth Forms */
|
||||
(ucs >= 0xffe0 && ucs <= 0xffe6) ||
|
||||
(ucs >= 0x20000 && ucs <= 0x2fffd) ||
|
||||
(ucs >= 0x30000 && ucs <= 0x3fffd)));
|
||||
}
|
||||
|
||||
static int mk_wcswidth(const wchar_t *pwcs, size_t n)
|
||||
{
|
||||
int w, width = 0;
|
||||
|
||||
for (;*pwcs && n-- > 0; pwcs++)
|
||||
if ((w = mk_wcwidth(*pwcs)) < 0)
|
||||
return -1;
|
||||
else
|
||||
width += w;
|
||||
|
||||
return width;
|
||||
}
|
||||
|
||||
#endif // HAVE_BROKEN_WCWIDTH
|
||||
|
||||
46
fallback.h
46
fallback.h
@@ -12,6 +12,10 @@
|
||||
#include <sys/types.h>
|
||||
#include <signal.h>
|
||||
|
||||
/** fish's internal versions of wcwidth and wcswidth, which can use an internal implementation if the system one is busted. */
|
||||
int fish_wcwidth(wchar_t wc);
|
||||
int fish_wcswidth(const wchar_t *str, size_t n);
|
||||
|
||||
#ifndef WCHAR_MAX
|
||||
/**
|
||||
This _should_ be defined by wchar.h, but e.g. OpenBSD doesn't.
|
||||
@@ -205,7 +209,20 @@ int wcwidth( wchar_t c );
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_WCSDUP
|
||||
|
||||
/** On OS X, use weak linking for wcsdup and wcscasecmp. Weak linking allows you to call the function only if it exists at runtime. You can detect it by testing the function pointer against NULL. To avoid making the callers do that, redefine wcsdup to wcsdup_use_weak, and likewise with wcscasecmp. This lets us use the same binary on SnowLeopard (10.6) and Lion+ (10.7), even though these functions only exist on 10.7+.
|
||||
|
||||
On other platforms, use what's detected at build time.
|
||||
*/
|
||||
#if __APPLE__ && __DARWIN_C_LEVEL >= 200809L
|
||||
wchar_t *wcsdup_use_weak(const wchar_t *);
|
||||
int wcscasecmp_use_weak(const wchar_t *, const wchar_t *);
|
||||
#define wcsdup(a) wcsdup_use_weak((a))
|
||||
#define wcscasecmp(a, b) wcscasecmp_use_weak((a), (b))
|
||||
|
||||
#else
|
||||
|
||||
#ifndef HAVE_WCSDUP
|
||||
|
||||
/**
|
||||
Create a duplicate string. Wide string version of strdup. Will
|
||||
@@ -213,18 +230,9 @@ int wcwidth( wchar_t c );
|
||||
*/
|
||||
wchar_t *wcsdup(const wchar_t *in);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_WCSLEN
|
||||
|
||||
/**
|
||||
Fallback for wcsen. Returns the length of the specified string.
|
||||
*/
|
||||
size_t wcslen(const wchar_t *in);
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_WCSCASECMP
|
||||
#ifndef HAVE_WCSCASECMP
|
||||
/**
|
||||
Case insensitive string compare function. Wide string version of
|
||||
strcasecmp.
|
||||
@@ -238,8 +246,20 @@ size_t wcslen(const wchar_t *in);
|
||||
*/
|
||||
int wcscasecmp( const wchar_t *a, const wchar_t *b );
|
||||
|
||||
#endif
|
||||
#endif //__APPLE__
|
||||
|
||||
|
||||
#ifndef HAVE_WCSLEN
|
||||
|
||||
/**
|
||||
Fallback for wclsen. Returns the length of the specified string.
|
||||
*/
|
||||
size_t wcslen(const wchar_t *in);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef HAVE_WCSNCASECMP
|
||||
|
||||
/**
|
||||
@@ -270,7 +290,7 @@ int wcsncasecmp( const wchar_t *a, const wchar_t *b, int count );
|
||||
Fallback for wcsndup function. Returns a copy of \c in, truncated
|
||||
to a maximum length of \c c.
|
||||
*/
|
||||
wchar_t *wcsndup( const wchar_t *in, int c );
|
||||
wchar_t *wcsndup( const wchar_t *in, size_t c );
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
206
fish.cpp
206
fish.cpp
@@ -32,6 +32,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
#include <unistd.h>
|
||||
#include <termios.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
#ifdef HAVE_GETOPT_H
|
||||
#include <getopt.h>
|
||||
@@ -61,20 +62,164 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
#include "history.h"
|
||||
#include "path.h"
|
||||
|
||||
/* PATH_MAX may not exist */
|
||||
#ifndef PATH_MAX
|
||||
#define PATH_MAX 1024
|
||||
#endif
|
||||
|
||||
/**
|
||||
The string describing the single-character options accepted by the main fish binary
|
||||
*/
|
||||
#define GETOPT_STRING "+hilnvc:p:d:"
|
||||
|
||||
static bool has_suffix(const std::string &path, const char *suffix, bool ignore_case)
|
||||
{
|
||||
size_t pathlen = path.size(), suffixlen = strlen(suffix);
|
||||
return pathlen >= suffixlen && ! (ignore_case ? strcasecmp : strcmp)(path.c_str() + pathlen - suffixlen, suffix);
|
||||
}
|
||||
|
||||
/* Modifies the given path by calling realpath. Returns true if realpath succeeded, false otherwise */
|
||||
static bool get_realpath(std::string &path)
|
||||
{
|
||||
char buff[PATH_MAX], *ptr;
|
||||
if ((ptr = realpath(path.c_str(), buff)))
|
||||
{
|
||||
path = ptr;
|
||||
}
|
||||
return ptr != NULL;
|
||||
}
|
||||
|
||||
/* OS X function for getting the executable path */
|
||||
extern "C" {
|
||||
int _NSGetExecutablePath(char* buf, uint32_t* bufsize);
|
||||
}
|
||||
|
||||
/* Return the path to the current executable. This needs to be realpath'd. */
|
||||
static std::string get_executable_path(const char *argv0)
|
||||
{
|
||||
char buff[PATH_MAX];
|
||||
#if __APPLE__
|
||||
{
|
||||
/* Returns 0 on success, -1 if the buffer is too small */
|
||||
uint32_t buffSize = sizeof buff;
|
||||
if (0 == _NSGetExecutablePath(buff, &buffSize))
|
||||
return std::string(buff);
|
||||
|
||||
/* Loop until we're big enough */
|
||||
char *mbuff = (char *)malloc(buffSize);
|
||||
while (0 > _NSGetExecutablePath(mbuff, &buffSize))
|
||||
mbuff = (char *)realloc(mbuff, buffSize);
|
||||
|
||||
/* Return the string */
|
||||
std::string result = mbuff;
|
||||
free(mbuff);
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
{
|
||||
/* On other Unixes, try /proc directory. This might be worth breaking out into macros. */
|
||||
if (0 < readlink("/proc/self/exe", buff, sizeof buff) || // Linux
|
||||
0 < readlink("/proc/curproc/file", buff, sizeof buff) || // BSD
|
||||
0 < readlink("/proc/self/path/a.out", buff, sizeof buff)) // Solaris
|
||||
{
|
||||
return std::string(buff);
|
||||
}
|
||||
}
|
||||
|
||||
/* Just return argv0, which probably won't work (i.e. it's not an absolute path or a path relative to the working directory, but instead something the caller found via $PATH). We'll eventually fall back to the compile time paths. */
|
||||
return std::string(argv0 ? argv0 : "");
|
||||
}
|
||||
|
||||
|
||||
static struct config_paths_t determine_config_directory_paths(const char *argv0)
|
||||
{
|
||||
struct config_paths_t paths;
|
||||
bool done = false;
|
||||
std::string exec_path = get_executable_path(argv0);
|
||||
if (get_realpath(exec_path))
|
||||
{
|
||||
#if __APPLE__
|
||||
|
||||
/* On OS X, maybe we're an app bundle, and should use the bundle's files. Since we don't link CF, use this lame approach to test it: see if the resolved path ends with /Contents/MacOS/fish, case insensitive since HFS+ usually is.
|
||||
*/
|
||||
if (! done)
|
||||
{
|
||||
const char *suffix = "/Contents/MacOS/fish";
|
||||
const size_t suffixlen = strlen(suffix);
|
||||
if (has_suffix(exec_path, suffix, true))
|
||||
{
|
||||
/* Looks like we're a bundle. Cut the string at the / prefixing /Contents... and then the rest */
|
||||
wcstring wide_resolved_path = str2wcstring(exec_path);
|
||||
wide_resolved_path.resize(exec_path.size() - suffixlen);
|
||||
wide_resolved_path.append(L"/Contents/Resources/");
|
||||
|
||||
/* Append share, etc, doc */
|
||||
paths.data = wide_resolved_path + L"share/fish";
|
||||
paths.sysconf = wide_resolved_path + L"etc/fish";
|
||||
paths.doc = wide_resolved_path + L"doc/fish";
|
||||
|
||||
/* But the bin_dir is the resolved_path, minus fish (aka the MacOS directory) */
|
||||
paths.bin = str2wcstring(exec_path);
|
||||
paths.bin.resize(paths.bin.size() - strlen("/fish"));
|
||||
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (! done)
|
||||
{
|
||||
/* The next check is that we are in a reloctable directory tree like this:
|
||||
bin/fish
|
||||
etc/fish
|
||||
share/fish
|
||||
|
||||
Check it!
|
||||
*/
|
||||
const char *suffix = "/bin/fish";
|
||||
if (has_suffix(exec_path, suffix, false))
|
||||
{
|
||||
wcstring base_path = str2wcstring(exec_path);
|
||||
base_path.resize(base_path.size() - strlen(suffix));
|
||||
|
||||
paths.data = base_path + L"/share/fish";
|
||||
paths.sysconf = base_path + L"/etc/fish";
|
||||
paths.doc = base_path + L"/share/doc/fish";
|
||||
paths.bin = base_path + L"/bin";
|
||||
|
||||
struct stat buf;
|
||||
if (0 == wstat(paths.data, &buf) && 0 == wstat(paths.sysconf, &buf))
|
||||
{
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (! done)
|
||||
{
|
||||
/* Fall back to what got compiled in. */
|
||||
paths.data = L"" DATADIR "/fish";
|
||||
paths.sysconf = L"" SYSCONFDIR "/fish";
|
||||
paths.doc = L"" DATADIR "/doc/fish";
|
||||
paths.bin = L"" PREFIX "/bin";
|
||||
|
||||
done = true;
|
||||
}
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
/**
|
||||
Parse init files
|
||||
Parse init files. exec_path is the path of fish executable as determined by argv[0].
|
||||
*/
|
||||
static int read_init()
|
||||
static int read_init(const struct config_paths_t &paths)
|
||||
{
|
||||
parser_t &parser = parser_t::principal_parser();
|
||||
const io_chain_t empty_ios;
|
||||
parser.eval( L"builtin . " + paths.data + L"/config.fish 2>/dev/null", empty_ios, TOP );
|
||||
parser.eval( L"builtin . " + paths.sysconf + L"/config.fish 2>/dev/null", empty_ios, TOP );
|
||||
|
||||
parser.eval( L"builtin . " DATADIR "/fish/config.fish 2>/dev/null", 0, TOP );
|
||||
parser.eval( L"builtin . " SYSCONFDIR L"/fish/config.fish 2>/dev/null", 0, TOP );
|
||||
|
||||
/*
|
||||
We need to get the configuration directory before we can source the user configuration file
|
||||
@@ -89,7 +234,7 @@ static int read_init()
|
||||
{
|
||||
wcstring config_dir_escaped = escape_string( config_dir, 1 );
|
||||
wcstring eval_buff = format_string(L"builtin . %ls/config.fish 2>/dev/null", config_dir_escaped.c_str());
|
||||
parser.eval( eval_buff, 0, TOP );
|
||||
parser.eval( eval_buff, empty_ios, TOP );
|
||||
}
|
||||
|
||||
return 1;
|
||||
@@ -176,14 +321,14 @@ static int fish_parse_opt( int argc, char **argv, const char **cmd_ptr )
|
||||
case 'd':
|
||||
{
|
||||
char *end;
|
||||
int tmp;
|
||||
long tmp;
|
||||
|
||||
errno = 0;
|
||||
tmp = strtol(optarg, &end, 10);
|
||||
|
||||
if( tmp >= 0 && tmp <=10 && !*end && !errno )
|
||||
{
|
||||
debug_level=tmp;
|
||||
debug_level = (int)tmp;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -265,22 +410,48 @@ static int fish_parse_opt( int argc, char **argv, const char **cmd_ptr )
|
||||
parses commands from stdin or files, depending on arguments
|
||||
*/
|
||||
|
||||
static wcstring full_escape( const wchar_t *in )
|
||||
{
|
||||
wcstring out;
|
||||
for( ; *in; in++ )
|
||||
{
|
||||
if( *in < 32 )
|
||||
{
|
||||
append_format( out, L"\\x%.2x", *in );
|
||||
}
|
||||
else if( *in < 128 )
|
||||
{
|
||||
out.push_back(*in);
|
||||
}
|
||||
else if( *in < 65536 )
|
||||
{
|
||||
append_format( out, L"\\u%.4x", *in );
|
||||
}
|
||||
else
|
||||
{
|
||||
append_format( out, L"\\U%.8x", *in );
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
extern int g_fork_count;
|
||||
int main( int argc, char **argv )
|
||||
{
|
||||
struct stat tmp;
|
||||
int res=1;
|
||||
const char *cmd=0;
|
||||
int my_optind=0;
|
||||
|
||||
set_main_thread();
|
||||
setup_fork_guards();
|
||||
save_term_foreground_process_group();
|
||||
|
||||
wsetlocale( LC_ALL, L"" );
|
||||
is_interactive_session=1;
|
||||
program_name=L"fish";
|
||||
|
||||
stat("----------FISH_HIT_MAIN----------", &tmp);
|
||||
//struct stat tmp;
|
||||
//stat("----------FISH_HIT_MAIN----------", &tmp);
|
||||
|
||||
my_optind = fish_parse_opt( argc, argv, &cmd );
|
||||
|
||||
@@ -292,14 +463,15 @@ int main( int argc, char **argv )
|
||||
debug( 1, _(L"Can not use the no-execute mode when running an interactive session") );
|
||||
no_exec = 0;
|
||||
}
|
||||
|
||||
|
||||
const struct config_paths_t paths = determine_config_directory_paths(argv[0]);
|
||||
|
||||
proc_init();
|
||||
event_init();
|
||||
wutil_init();
|
||||
//parser_init();
|
||||
builtin_init();
|
||||
function_init();
|
||||
env_init();
|
||||
env_init(&paths);
|
||||
reader_init();
|
||||
history_init();
|
||||
|
||||
@@ -308,12 +480,13 @@ int main( int argc, char **argv )
|
||||
if (g_log_forks)
|
||||
printf("%d: g_fork_count: %d\n", __LINE__, g_fork_count);
|
||||
|
||||
if( read_init() )
|
||||
const io_chain_t empty_ios;
|
||||
if( read_init(paths) )
|
||||
{
|
||||
if( cmd != 0 )
|
||||
{
|
||||
wchar_t *cmd_wcs = str2wcs( cmd );
|
||||
res = parser.eval( cmd_wcs, 0, TOP );
|
||||
res = parser.eval( cmd_wcs, empty_ios, TOP );
|
||||
free(cmd_wcs);
|
||||
reader_exit(0, 0);
|
||||
}
|
||||
@@ -321,7 +494,7 @@ int main( int argc, char **argv )
|
||||
{
|
||||
if( my_optind == argc )
|
||||
{
|
||||
res = reader_read( STDIN_FILENO, 0 );
|
||||
res = reader_read( STDIN_FILENO, empty_ios );
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -366,7 +539,7 @@ int main( int argc, char **argv )
|
||||
free( rel_filename );
|
||||
free( abs_filename );
|
||||
|
||||
res = reader_read( fd, 0 );
|
||||
res = reader_read( fd, empty_ios );
|
||||
|
||||
if( res )
|
||||
{
|
||||
@@ -381,6 +554,7 @@ int main( int argc, char **argv )
|
||||
|
||||
proc_fire_event( L"PROCESS_EXIT", EVENT_EXIT, getpid(), res );
|
||||
|
||||
restore_term_foreground_process_group();
|
||||
history_destroy();
|
||||
proc_destroy();
|
||||
builtin_destroy();
|
||||
|
||||
@@ -6,9 +6,9 @@ Release: 0%{?dist}
|
||||
|
||||
License: GPL
|
||||
Group: System Environment/Shells
|
||||
URL: http://www.fishshell.org
|
||||
URL: http://ridiculousfish.com/shell/
|
||||
|
||||
Source0: http://www.fishshell.org/files/%{version}/%{name}-%{version}.tar.bz2
|
||||
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
|
||||
|
||||
@@ -74,7 +74,8 @@ static void read_file( FILE *f, wcstring &b )
|
||||
*/
|
||||
static void insert_tabs( wcstring &out, int indent )
|
||||
{
|
||||
out.append(indent, L'\t');
|
||||
if (indent > 0)
|
||||
out.append((size_t)indent, L'\t');
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -77,7 +77,8 @@ enum
|
||||
HIGHLIGHT_PAGER_PREFIX,
|
||||
HIGHLIGHT_PAGER_COMPLETION,
|
||||
HIGHLIGHT_PAGER_DESCRIPTION,
|
||||
HIGHLIGHT_PAGER_PROGRESS
|
||||
HIGHLIGHT_PAGER_PROGRESS,
|
||||
HIGHLIGHT_PAGER_SECONDARY
|
||||
}
|
||||
;
|
||||
|
||||
@@ -152,7 +153,8 @@ static const wchar_t *hightlight_var[] =
|
||||
L"fish_pager_color_prefix",
|
||||
L"fish_pager_color_completion",
|
||||
L"fish_pager_color_description",
|
||||
L"fish_pager_color_progress"
|
||||
L"fish_pager_color_progress",
|
||||
L"fish_pager_color_secondary"
|
||||
}
|
||||
;
|
||||
|
||||
@@ -206,7 +208,7 @@ static rgb_color_t get_color( int highlight )
|
||||
|
||||
if( highlight < 0 )
|
||||
return rgb_color_t::normal();
|
||||
if( highlight >= (4) )
|
||||
if( highlight >= (5) )
|
||||
return rgb_color_t::normal();
|
||||
|
||||
val = wgetenv( hightlight_var[highlight]);
|
||||
@@ -389,7 +391,7 @@ static int print_max( const wchar_t *str, int max, int has_more )
|
||||
/**
|
||||
Print the specified item using at the specified amount of space
|
||||
*/
|
||||
static void completion_print_item( const wchar_t *prefix, comp_t *c, int width )
|
||||
static void completion_print_item( const wchar_t *prefix, comp_t *c, int width, bool secondary )
|
||||
{
|
||||
int comp_width=0, desc_width=0;
|
||||
int written=0;
|
||||
@@ -422,14 +424,15 @@ static void completion_print_item( const wchar_t *prefix, comp_t *c, int width )
|
||||
|
||||
}
|
||||
|
||||
rgb_color_t bg = secondary ? get_color(HIGHLIGHT_PAGER_SECONDARY) : rgb_color_t::normal();
|
||||
for( size_t i=0; i<c->comp.size(); i++ )
|
||||
{
|
||||
const wcstring &comp = c->comp.at(i);
|
||||
if( i != 0 )
|
||||
written += print_max( L" ", comp_width - written, 2 );
|
||||
set_color( get_color(HIGHLIGHT_PAGER_PREFIX),rgb_color_t::normal() );
|
||||
set_color( get_color(HIGHLIGHT_PAGER_PREFIX), bg );
|
||||
written += print_max( prefix, comp_width - written, comp.empty()?0:1 );
|
||||
set_color( get_color(HIGHLIGHT_PAGER_COMPLETION),rgb_color_t::ignore() );
|
||||
set_color( get_color(HIGHLIGHT_PAGER_COMPLETION), bg);
|
||||
written += print_max( comp.c_str(), comp_width - written, i!=(c->comp.size()-1) );
|
||||
}
|
||||
|
||||
@@ -441,8 +444,8 @@ static void completion_print_item( const wchar_t *prefix, comp_t *c, int width )
|
||||
written++;
|
||||
writech( L' ');
|
||||
}
|
||||
set_color( get_color( HIGHLIGHT_PAGER_DESCRIPTION ), bg);
|
||||
written += print_max( L"(", 1, 0 );
|
||||
set_color( get_color( HIGHLIGHT_PAGER_DESCRIPTION ), rgb_color_t::ignore() );
|
||||
written += print_max( c->desc.c_str(), desc_width, 0 );
|
||||
written += print_max( L")", 1, 0 );
|
||||
}
|
||||
@@ -454,7 +457,8 @@ static void completion_print_item( const wchar_t *prefix, comp_t *c, int width )
|
||||
writech( L' ');
|
||||
}
|
||||
}
|
||||
|
||||
if ( secondary )
|
||||
set_color( rgb_color_t::normal(), rgb_color_t::normal() );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -476,11 +480,11 @@ static void completion_print( int cols,
|
||||
int row_stop,
|
||||
wchar_t *prefix,
|
||||
int is_quoted,
|
||||
std::vector<comp_t *> &lst )
|
||||
const std::vector<comp_t *> &lst )
|
||||
{
|
||||
|
||||
int rows = (lst.size()-1)/cols+1;
|
||||
int i, j;
|
||||
size_t rows = (lst.size()-1)/cols+1;
|
||||
size_t i, j;
|
||||
|
||||
for( i = row_start; i<row_stop; i++ )
|
||||
{
|
||||
@@ -490,12 +494,12 @@ static void completion_print( int cols,
|
||||
|
||||
int is_last = (j==(cols-1));
|
||||
|
||||
if( (int)lst.size() <= j*rows + i )
|
||||
if( lst.size() <= j*rows + i )
|
||||
continue;
|
||||
|
||||
el = lst.at(j*rows + i );
|
||||
|
||||
completion_print_item( prefix, el, width[j] - (is_last?0:2) );
|
||||
completion_print_item( prefix, el, width[j] - (is_last?0:2), i%2 );
|
||||
|
||||
if( !is_last)
|
||||
writestr( L" " );
|
||||
@@ -545,9 +549,9 @@ static int completion_try_print( int cols,
|
||||
*/
|
||||
int print=0;
|
||||
|
||||
int i, j;
|
||||
long i, j;
|
||||
|
||||
int rows = (lst.size()-1)/cols+1;
|
||||
int rows = (int)((lst.size()-1)/cols+1);
|
||||
|
||||
int pref_tot_width=0;
|
||||
int min_tot_width = 0;
|
||||
@@ -569,7 +573,7 @@ static int completion_try_print( int cols,
|
||||
{
|
||||
int pref,min;
|
||||
comp_t *c;
|
||||
if( (int)lst.size() <= j*rows + i )
|
||||
if( lst.size() <= j*rows + i )
|
||||
continue;
|
||||
|
||||
c = lst.at(j*rows + i );
|
||||
@@ -609,7 +613,7 @@ static int completion_try_print( int cols,
|
||||
}
|
||||
else
|
||||
{
|
||||
int next_rows = (lst.size()-1)/(cols-1)+1;
|
||||
long next_rows = (lst.size()-1)/(cols-1)+1;
|
||||
/* fwprintf( stderr,
|
||||
L"cols %d, min_tot %d, term %d, rows=%d, nextrows %d, termrows %d, diff %d\n",
|
||||
cols,
|
||||
@@ -754,8 +758,7 @@ static int completion_try_print( int cols,
|
||||
case PAGE_DOWN:
|
||||
{
|
||||
|
||||
npos = mini( rows - termsize.ws_row+1,
|
||||
pos + termsize.ws_row-1 );
|
||||
npos = mini( (int)(rows - termsize.ws_row+1), (int)(pos + termsize.ws_row-1) );
|
||||
if( npos != pos )
|
||||
{
|
||||
pos = npos;
|
||||
@@ -869,11 +872,9 @@ static void mangle_descriptions( wcstring_list_t &lst )
|
||||
*/
|
||||
static void join_completions( wcstring_list_t lst )
|
||||
{
|
||||
long i;
|
||||
|
||||
std::map<wcstring, long> desc_table;
|
||||
|
||||
for( i=0; i<(long)lst.size(); i++ )
|
||||
for( size_t i=0; i<lst.size(); i++ )
|
||||
{
|
||||
const wchar_t *item = lst.at(i).c_str();
|
||||
const wchar_t *desc = wcschr( item, COMPLETE_SEP );
|
||||
@@ -885,7 +886,7 @@ static void join_completions( wcstring_list_t lst )
|
||||
prev_idx = desc_table[desc] - 1;
|
||||
if( prev_idx == -1 )
|
||||
{
|
||||
desc_table[desc] = i+1;
|
||||
desc_table[desc] = (long)(i+1);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -950,7 +951,7 @@ static std::vector<comp_t *> mangle_completions( wcstring_list_t &lst, const wch
|
||||
|
||||
}
|
||||
|
||||
comp->comp_width += my_wcswidth(prefix)*comp->comp.size() + 2*(comp->comp.size()-1);
|
||||
comp->comp_width += (int)(my_wcswidth(prefix)*comp->comp.size() + 2*(comp->comp.size()-1));
|
||||
comp->desc_width = comp->desc.empty()?0:my_wcswidth( comp->desc.c_str() );
|
||||
|
||||
comp->pref_width = comp->comp_width + comp->desc_width + (comp->desc_width?4:0);
|
||||
|
||||
280
fish_tests.cpp
280
fish_tests.cpp
@@ -59,6 +59,8 @@
|
||||
#include "iothread.h"
|
||||
#include "postfork.h"
|
||||
#include "signal.h"
|
||||
#include "highlight.h"
|
||||
|
||||
/**
|
||||
The number of tests to run
|
||||
*/
|
||||
@@ -312,10 +314,10 @@ static void test_tok()
|
||||
|
||||
{
|
||||
|
||||
const wchar_t *str = L"string <redirection 2>&1 'nested \"quoted\" '(string containing subshells ){and,brackets}$as[$well (as variable arrays)]";
|
||||
const wchar_t *str = L"string <redirection 2>&1 'nested \"quoted\" '(string containing subshells ){and,brackets}$as[$well (as variable arrays)] not_a_redirect^ ^ ^^is_a_redirect";
|
||||
const int types[] =
|
||||
{
|
||||
TOK_STRING, TOK_REDIRECT_IN, TOK_STRING, TOK_REDIRECT_FD, TOK_STRING, TOK_STRING, TOK_END
|
||||
TOK_STRING, TOK_REDIRECT_IN, TOK_STRING, TOK_REDIRECT_FD, TOK_STRING, TOK_STRING, TOK_STRING, TOK_REDIRECT_OUT, TOK_REDIRECT_APPEND, TOK_STRING, TOK_END
|
||||
}
|
||||
;
|
||||
size_t i;
|
||||
@@ -432,6 +434,15 @@ static void test_parser()
|
||||
{
|
||||
err( L"'else' command outside of conditional block context undetected" );
|
||||
}
|
||||
if( !parser.test( L"else if", 0, 0, 0 ) )
|
||||
{
|
||||
err( L"'else if' command outside of conditional block context undetected" );
|
||||
}
|
||||
if( !parser.test( L"if false; else if; end", 0, 0, 0 ) )
|
||||
{
|
||||
err( L"'else if' missing command undetected" );
|
||||
}
|
||||
|
||||
if( !parser.test( L"break", 0, 0, 0 ) )
|
||||
{
|
||||
err( L"'break' command outside of loop block context undetected" );
|
||||
@@ -449,7 +460,7 @@ static void test_parser()
|
||||
err( L"Null input when evaluating undetected" );
|
||||
}
|
||||
#endif
|
||||
if( !parser.eval( L"ls", 0, WHILE ) )
|
||||
if( !parser.eval( L"ls", io_chain_t(), WHILE ) )
|
||||
{
|
||||
err( L"Invalid block mode when evaluating undetected" );
|
||||
}
|
||||
@@ -516,7 +527,13 @@ static int expand_test( const wchar_t *in, int flags, ... )
|
||||
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
for (size_t idx=0; idx < output.size(); idx++)
|
||||
{
|
||||
printf("%ls\n", output.at(idx).completion.c_str());
|
||||
}
|
||||
#endif
|
||||
|
||||
va_start( va, flags );
|
||||
|
||||
while( (arg=va_arg(va, wchar_t *) )!= 0 )
|
||||
@@ -562,7 +579,22 @@ static void test_expand()
|
||||
{
|
||||
err( L"Cannot skip wildcard expansion" );
|
||||
}
|
||||
|
||||
|
||||
if (system("mkdir -p /tmp/fish_expand_test/")) err(L"mkdir failed");
|
||||
if (system("touch /tmp/fish_expand_test/.foo")) err(L"touch failed");
|
||||
if (system("touch /tmp/fish_expand_test/bar")) err(L"touch failed");
|
||||
|
||||
// This is checking that .* does NOT match . and .. (https://github.com/fish-shell/fish-shell/issues/270). But it does have to match literal components (e.g. "./*" has to match the same as "*"
|
||||
if (! expand_test( L"/tmp/fish_expand_test/.*", 0, L"/tmp/fish_expand_test/.foo", 0 ))
|
||||
{
|
||||
err( L"Expansion not correctly handling dotfiles" );
|
||||
}
|
||||
if (! expand_test( L"/tmp/fish_expand_test/./.*", 0, L"/tmp/fish_expand_test/./.foo", 0 ))
|
||||
{
|
||||
err( L"Expansion not correctly handling literal path components in dotfiles" );
|
||||
}
|
||||
|
||||
//system("rm -Rf /tmp/fish_expand_test");
|
||||
}
|
||||
|
||||
/** Test path functions */
|
||||
@@ -606,24 +638,24 @@ static void test_is_potential_path()
|
||||
const wcstring_list_t wds(1, wd);
|
||||
|
||||
wcstring tmp;
|
||||
assert(is_potential_path(L"al", wds, true, &tmp) && tmp == L"alpha/");
|
||||
assert(is_potential_path(L"alpha/", wds, true, &tmp) && tmp == L"alpha/");
|
||||
assert(is_potential_path(L"aard", wds, false, &tmp) && tmp == L"aardvark");
|
||||
assert(is_potential_path(L"al", wds, PATH_REQUIRE_DIR, &tmp) && tmp == L"alpha/");
|
||||
assert(is_potential_path(L"alpha/", wds, PATH_REQUIRE_DIR, &tmp) && tmp == L"alpha/");
|
||||
assert(is_potential_path(L"aard", wds, 0, &tmp) && tmp == L"aardvark");
|
||||
|
||||
assert(! is_potential_path(L"balpha/", wds, true, &tmp));
|
||||
assert(! is_potential_path(L"aard", wds, true, &tmp));
|
||||
assert(! is_potential_path(L"aarde", wds, true, &tmp));
|
||||
assert(! is_potential_path(L"aarde", wds, false, &tmp));
|
||||
assert(! is_potential_path(L"balpha/", wds, PATH_REQUIRE_DIR, &tmp));
|
||||
assert(! is_potential_path(L"aard", wds, PATH_REQUIRE_DIR, &tmp));
|
||||
assert(! is_potential_path(L"aarde", wds, PATH_REQUIRE_DIR, &tmp));
|
||||
assert(! is_potential_path(L"aarde", wds, 0, &tmp));
|
||||
|
||||
assert(is_potential_path(L"/tmp/is_potential_path_test/aardvark", wds, false, &tmp) && tmp == L"/tmp/is_potential_path_test/aardvark");
|
||||
assert(is_potential_path(L"/tmp/is_potential_path_test/al", wds, true, &tmp) && tmp == L"/tmp/is_potential_path_test/alpha/");
|
||||
assert(is_potential_path(L"/tmp/is_potential_path_test/aardv", wds, false, &tmp) && tmp == L"/tmp/is_potential_path_test/aardvark");
|
||||
assert(is_potential_path(L"/tmp/is_potential_path_test/aardvark", wds, 0, &tmp) && tmp == L"/tmp/is_potential_path_test/aardvark");
|
||||
assert(is_potential_path(L"/tmp/is_potential_path_test/al", wds, PATH_REQUIRE_DIR, &tmp) && tmp == L"/tmp/is_potential_path_test/alpha/");
|
||||
assert(is_potential_path(L"/tmp/is_potential_path_test/aardv", wds, 0, &tmp) && tmp == L"/tmp/is_potential_path_test/aardvark");
|
||||
|
||||
assert(! is_potential_path(L"/tmp/is_potential_path_test/aardvark", wds, true, &tmp));
|
||||
assert(! is_potential_path(L"/tmp/is_potential_path_test/al/", wds, false, &tmp));
|
||||
assert(! is_potential_path(L"/tmp/is_potential_path_test/ar", wds, false, &tmp));
|
||||
assert(! is_potential_path(L"/tmp/is_potential_path_test/aardvark", wds, PATH_REQUIRE_DIR, &tmp));
|
||||
assert(! is_potential_path(L"/tmp/is_potential_path_test/al/", wds, 0, &tmp));
|
||||
assert(! is_potential_path(L"/tmp/is_potential_path_test/ar", wds, 0, &tmp));
|
||||
|
||||
assert(is_potential_path(L"/usr", wds, true, &tmp) && tmp == L"/usr/");
|
||||
assert(is_potential_path(L"/usr", wds, PATH_REQUIRE_DIR, &tmp) && tmp == L"/usr/");
|
||||
|
||||
}
|
||||
|
||||
@@ -727,9 +759,84 @@ static void test_colors()
|
||||
assert(rgb_color_t(L"mooganta").is_none());
|
||||
}
|
||||
|
||||
/* Testing autosuggestion */
|
||||
static void test_autosuggest() {
|
||||
bool autosuggest_special_validate_from_history(const wcstring &str, const wcstring &working_directory, bool *outSuggestionOK);
|
||||
static void perform_one_autosuggestion_test(const wcstring &command, const wcstring &wd, const wcstring &expected, long line)
|
||||
{
|
||||
wcstring suggestion;
|
||||
bool success = autosuggest_suggest_special(command, wd, suggestion);
|
||||
if (! success)
|
||||
{
|
||||
printf("line %ld: autosuggest_suggest_special() failed for command %ls\n", line, command.c_str());
|
||||
assert(success);
|
||||
}
|
||||
if (suggestion != expected)
|
||||
{
|
||||
printf("line %ld: autosuggest_suggest_special() returned the wrong expected string for command %ls\n", line, command.c_str());
|
||||
printf(" actual: %ls\n", suggestion.c_str());
|
||||
printf("expected: %ls\n", expected.c_str());
|
||||
assert(suggestion == expected);
|
||||
}
|
||||
}
|
||||
|
||||
/* Testing test_autosuggest_suggest_special, in particular for properly handling quotes and backslashes */
|
||||
static void test_autosuggest_suggest_special() {
|
||||
if (system("mkdir -p '/tmp/autosuggest_test/0foobar'")) err(L"mkdir failed");
|
||||
if (system("mkdir -p '/tmp/autosuggest_test/1foo bar'")) err(L"mkdir failed");
|
||||
if (system("mkdir -p '/tmp/autosuggest_test/2foo bar'")) err(L"mkdir failed");
|
||||
if (system("mkdir -p '/tmp/autosuggest_test/3foo\\bar'")) err(L"mkdir failed");
|
||||
if (system("mkdir -p /tmp/autosuggest_test/4foo\\'bar")) err(L"mkdir failed"); //a path with a single quote
|
||||
if (system("mkdir -p /tmp/autosuggest_test/5foo\\\"bar")) err(L"mkdir failed"); //a path with a double quote
|
||||
if (system("mkdir -p ~/test_autosuggest_suggest_special/")) err(L"mkdir failed"); //make sure tilde is handled
|
||||
|
||||
const wcstring wd = L"/tmp/autosuggest_test/";
|
||||
perform_one_autosuggestion_test(L"cd /tmp/autosuggest_test/0", wd, L"cd /tmp/autosuggest_test/0foobar/", __LINE__);
|
||||
perform_one_autosuggestion_test(L"cd \"/tmp/autosuggest_test/0", wd, L"cd \"/tmp/autosuggest_test/0foobar/\"", __LINE__);
|
||||
perform_one_autosuggestion_test(L"cd '/tmp/autosuggest_test/0", wd, L"cd '/tmp/autosuggest_test/0foobar/'", __LINE__);
|
||||
perform_one_autosuggestion_test(L"cd 0", wd, L"cd 0foobar/", __LINE__);
|
||||
perform_one_autosuggestion_test(L"cd \"0", wd, L"cd \"0foobar/\"", __LINE__);
|
||||
perform_one_autosuggestion_test(L"cd '0", wd, L"cd '0foobar/'", __LINE__);
|
||||
|
||||
perform_one_autosuggestion_test(L"cd /tmp/autosuggest_test/1", wd, L"cd /tmp/autosuggest_test/1foo\\ bar/", __LINE__);
|
||||
perform_one_autosuggestion_test(L"cd \"/tmp/autosuggest_test/1", wd, L"cd \"/tmp/autosuggest_test/1foo bar/\"", __LINE__);
|
||||
perform_one_autosuggestion_test(L"cd '/tmp/autosuggest_test/1", wd, L"cd '/tmp/autosuggest_test/1foo bar/'", __LINE__);
|
||||
perform_one_autosuggestion_test(L"cd 1", wd, L"cd 1foo\\ bar/", __LINE__);
|
||||
perform_one_autosuggestion_test(L"cd \"1", wd, L"cd \"1foo bar/\"", __LINE__);
|
||||
perform_one_autosuggestion_test(L"cd '1", wd, L"cd '1foo bar/'", __LINE__);
|
||||
|
||||
perform_one_autosuggestion_test(L"cd /tmp/autosuggest_test/2", wd, L"cd /tmp/autosuggest_test/2foo\\ \\ bar/", __LINE__);
|
||||
perform_one_autosuggestion_test(L"cd \"/tmp/autosuggest_test/2", wd, L"cd \"/tmp/autosuggest_test/2foo bar/\"", __LINE__);
|
||||
perform_one_autosuggestion_test(L"cd '/tmp/autosuggest_test/2", wd, L"cd '/tmp/autosuggest_test/2foo bar/'", __LINE__);
|
||||
perform_one_autosuggestion_test(L"cd 2", wd, L"cd 2foo\\ \\ bar/", __LINE__);
|
||||
perform_one_autosuggestion_test(L"cd \"2", wd, L"cd \"2foo bar/\"", __LINE__);
|
||||
perform_one_autosuggestion_test(L"cd '2", wd, L"cd '2foo bar/'", __LINE__);
|
||||
|
||||
perform_one_autosuggestion_test(L"cd /tmp/autosuggest_test/3", wd, L"cd /tmp/autosuggest_test/3foo\\\\bar/", __LINE__);
|
||||
perform_one_autosuggestion_test(L"cd \"/tmp/autosuggest_test/3", wd, L"cd \"/tmp/autosuggest_test/3foo\\bar/\"", __LINE__);
|
||||
perform_one_autosuggestion_test(L"cd '/tmp/autosuggest_test/3", wd, L"cd '/tmp/autosuggest_test/3foo\\bar/'", __LINE__);
|
||||
perform_one_autosuggestion_test(L"cd 3", wd, L"cd 3foo\\\\bar/", __LINE__);
|
||||
perform_one_autosuggestion_test(L"cd \"3", wd, L"cd \"3foo\\bar/\"", __LINE__);
|
||||
perform_one_autosuggestion_test(L"cd '3", wd, L"cd '3foo\\bar/'", __LINE__);
|
||||
|
||||
perform_one_autosuggestion_test(L"cd /tmp/autosuggest_test/4", wd, L"cd /tmp/autosuggest_test/4foo\\'bar/", __LINE__);
|
||||
perform_one_autosuggestion_test(L"cd \"/tmp/autosuggest_test/4", wd, L"cd \"/tmp/autosuggest_test/4foo'bar/\"", __LINE__);
|
||||
perform_one_autosuggestion_test(L"cd '/tmp/autosuggest_test/4", wd, L"cd '/tmp/autosuggest_test/4foo\\'bar/'", __LINE__);
|
||||
perform_one_autosuggestion_test(L"cd 4", wd, L"cd 4foo\\'bar/", __LINE__);
|
||||
perform_one_autosuggestion_test(L"cd \"4", wd, L"cd \"4foo'bar/\"", __LINE__);
|
||||
perform_one_autosuggestion_test(L"cd '4", wd, L"cd '4foo\\'bar/'", __LINE__);
|
||||
|
||||
perform_one_autosuggestion_test(L"cd /tmp/autosuggest_test/5", wd, L"cd /tmp/autosuggest_test/5foo\\\"bar/", __LINE__);
|
||||
perform_one_autosuggestion_test(L"cd \"/tmp/autosuggest_test/5", wd, L"cd \"/tmp/autosuggest_test/5foo\\\"bar/\"", __LINE__);
|
||||
perform_one_autosuggestion_test(L"cd '/tmp/autosuggest_test/5", wd, L"cd '/tmp/autosuggest_test/5foo\"bar/'", __LINE__);
|
||||
perform_one_autosuggestion_test(L"cd 5", wd, L"cd 5foo\\\"bar/", __LINE__);
|
||||
perform_one_autosuggestion_test(L"cd \"5", wd, L"cd \"5foo\\\"bar/\"", __LINE__);
|
||||
perform_one_autosuggestion_test(L"cd '5", wd, L"cd '5foo\"bar/'", __LINE__);
|
||||
|
||||
perform_one_autosuggestion_test(L"cd ~/test_autosuggest_suggest_specia", wd, L"cd ~/test_autosuggest_suggest_special/", __LINE__);
|
||||
|
||||
// A single quote should defeat tilde expansion
|
||||
perform_one_autosuggestion_test(L"cd '~/test_autosuggest_suggest_specia'", wd, L"", __LINE__);
|
||||
|
||||
system("rm -Rf '/tmp/autosuggest_test/'");
|
||||
system("rm -Rf ~/test_autosuggest_suggest_special/");
|
||||
}
|
||||
|
||||
|
||||
@@ -833,6 +940,7 @@ class history_tests_t {
|
||||
public:
|
||||
static void test_history(void);
|
||||
static void test_history_merge(void);
|
||||
static void test_history_formats(void);
|
||||
};
|
||||
|
||||
static wcstring random_string(void) {
|
||||
@@ -886,7 +994,7 @@ void history_tests_t::test_history(void) {
|
||||
path_list_t paths;
|
||||
size_t count = rand() % 6;
|
||||
while (count--) {
|
||||
paths.push_front(random_string());
|
||||
paths.push_back(random_string());
|
||||
}
|
||||
|
||||
/* Record this item */
|
||||
@@ -975,6 +1083,129 @@ void history_tests_t::test_history_merge(void) {
|
||||
delete everything; //not as scary as it looks
|
||||
}
|
||||
|
||||
static bool install_sample_history(const wchar_t *name) {
|
||||
char command[512];
|
||||
snprintf(command, sizeof command, "cp tests/%ls ~/.config/fish/%ls_history", name, name);
|
||||
if (system(command)) {
|
||||
err(L"Failed to copy sample history");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Indicates whether the history is equal to the given null-terminated array of strings. */
|
||||
static bool history_equals(history_t &hist, const wchar_t * const *strings) {
|
||||
/* Count our expected items */
|
||||
size_t expected_count = 0;
|
||||
while (strings[expected_count]) {
|
||||
expected_count++;
|
||||
}
|
||||
|
||||
/* Ensure the contents are the same */
|
||||
size_t history_idx = 1;
|
||||
size_t array_idx = 0;
|
||||
for (;;) {
|
||||
const wchar_t *expected = strings[array_idx];
|
||||
history_item_t item = hist.item_at_index(history_idx);
|
||||
if (expected == NULL) {
|
||||
if (! item.empty()) {
|
||||
err(L"Expected empty item at history index %lu", history_idx);
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
if (item.str() != expected) {
|
||||
err(L"Expected '%ls', found '%ls' at index %lu", expected, item.str().c_str(), history_idx);
|
||||
}
|
||||
}
|
||||
history_idx++;
|
||||
array_idx++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void history_tests_t::test_history_formats(void) {
|
||||
const wchar_t *name;
|
||||
|
||||
// Test inferring and reading legacy and bash history formats
|
||||
name = L"history_sample_fish_1_x";
|
||||
say(L"Testing %ls", name);
|
||||
if (! install_sample_history(name)) {
|
||||
err(L"Couldn't open file tests/%ls", name);
|
||||
} else {
|
||||
/* Note: This is backwards from what appears in the file */
|
||||
const wchar_t * const expected[] = {
|
||||
L"#def",
|
||||
|
||||
L"echo #abc",
|
||||
|
||||
L"function yay\n"
|
||||
"echo hi\n"
|
||||
"end",
|
||||
|
||||
L"cd foobar",
|
||||
|
||||
L"ls /",
|
||||
|
||||
NULL
|
||||
};
|
||||
|
||||
history_t &test_history = history_t::history_with_name(name);
|
||||
if (! history_equals(test_history, expected)) {
|
||||
err(L"test_history_formats failed for %ls\n", name);
|
||||
}
|
||||
test_history.clear();
|
||||
}
|
||||
|
||||
name = L"history_sample_fish_2_0";
|
||||
say(L"Testing %ls", name);
|
||||
if (! install_sample_history(name)) {
|
||||
err(L"Couldn't open file tests/%ls", name);
|
||||
} else {
|
||||
const wchar_t * const expected[] = {
|
||||
L"echo this has\\\nbackslashes",
|
||||
|
||||
L"function foo\n"
|
||||
"echo bar\n"
|
||||
"end",
|
||||
|
||||
L"echo alpha",
|
||||
|
||||
NULL
|
||||
};
|
||||
|
||||
history_t &test_history = history_t::history_with_name(name);
|
||||
if (! history_equals(test_history, expected)) {
|
||||
err(L"test_history_formats failed for %ls\n", name);
|
||||
}
|
||||
test_history.clear();
|
||||
}
|
||||
|
||||
say(L"Testing bash import");
|
||||
FILE *f = fopen("tests/history_sample_bash", "r");
|
||||
if (! f) {
|
||||
err(L"Couldn't open file tests/history_sample_bash");
|
||||
} else {
|
||||
// It should skip over the export command since that's a bash-ism
|
||||
const wchar_t *expected[] = {
|
||||
L"echo supsup",
|
||||
|
||||
L"history --help",
|
||||
|
||||
L"echo foo",
|
||||
|
||||
NULL
|
||||
};
|
||||
history_t &test_history = history_t::history_with_name(L"bash_import");
|
||||
test_history.populate_from_bash(f);
|
||||
if (! history_equals(test_history, expected)) {
|
||||
err(L"test_history_formats failed for bash import\n");
|
||||
}
|
||||
test_history.clear();
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Main test
|
||||
@@ -1010,9 +1241,10 @@ int main( int argc, char **argv )
|
||||
test_path();
|
||||
test_is_potential_path();
|
||||
test_colors();
|
||||
test_autosuggest();
|
||||
test_autosuggest_suggest_special();
|
||||
history_tests_t::test_history();
|
||||
history_tests_t::test_history_merge();
|
||||
history_tests_t::test_history_formats();
|
||||
|
||||
say( L"Encountered %d errors in low-level tests", err_count );
|
||||
|
||||
|
||||
109
fishd.cpp
109
fishd.cpp
@@ -197,41 +197,6 @@ static void handle_term( int signal )
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Writes a pid_t in decimal representation to str.
|
||||
str must contain sufficient space.
|
||||
The conservatively approximate maximum number of characters a pid_t will
|
||||
represent is given by: (int)(0.31 * sizeof(pid_t) + CHAR_BIT + 1)
|
||||
Returns the length of the string
|
||||
*/
|
||||
static int sprint_pid_t( pid_t pid, char *str )
|
||||
{
|
||||
int len, i = 0;
|
||||
int dig;
|
||||
|
||||
/* Store digits in reverse order into string */
|
||||
while( pid != 0 )
|
||||
{
|
||||
dig = pid % 10;
|
||||
str[i] = '0' + dig;
|
||||
pid = ( pid - dig ) / 10;
|
||||
i++;
|
||||
}
|
||||
len = i;
|
||||
/* Reverse digits */
|
||||
i /= 2;
|
||||
while( i )
|
||||
{
|
||||
i--;
|
||||
dig = str[i];
|
||||
str[i] = str[len - 1 - i];
|
||||
str[len - 1 - i] = dig;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
Writes a pseudo-random number (between one and maxlen) of pseudo-random
|
||||
digits into str.
|
||||
@@ -247,7 +212,7 @@ static int sprint_pid_t( pid_t pid, char *str )
|
||||
Excludes chars other than digits since ANSI C only guarantees that digits
|
||||
are consecutive.
|
||||
*/
|
||||
static int sprint_rand_digits( char *str, int maxlen )
|
||||
static void sprint_rand_digits( char *str, int maxlen )
|
||||
{
|
||||
int i, max;
|
||||
struct timeval tv;
|
||||
@@ -259,13 +224,14 @@ static int sprint_rand_digits( char *str, int maxlen )
|
||||
Cast to unsigned so that wrapping occurs on overflow as per ANSI C.
|
||||
*/
|
||||
(void)gettimeofday( &tv, NULL );
|
||||
srand( (unsigned int)tv.tv_sec + (unsigned int)tv.tv_usec * 1000000UL );
|
||||
max = 1 + (maxlen - 1) * (rand() / (RAND_MAX + 1.0));
|
||||
unsigned long long seed = tv.tv_sec + tv.tv_usec * 1000000ULL;
|
||||
srand( (unsigned int)seed );
|
||||
max = (int)(1 + (maxlen - 1) * (rand() / (RAND_MAX + 1.0)));
|
||||
for( i = 0; i < max; i++ )
|
||||
{
|
||||
str[i] = '0' + 10 * (rand() / (RAND_MAX + 1.0));
|
||||
}
|
||||
return i;
|
||||
str[i] = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -278,45 +244,22 @@ static int sprint_rand_digits( char *str, int maxlen )
|
||||
fallback.
|
||||
The memory returned should be freed using free().
|
||||
*/
|
||||
static char *gen_unique_nfs_filename( const char *filename )
|
||||
static std::string gen_unique_nfs_filename( const char *filename )
|
||||
{
|
||||
int pidlen, hnlen, orglen = strlen( filename );
|
||||
char hostname[HOST_NAME_MAX + 1];
|
||||
char *newname;
|
||||
char hostname[HOST_NAME_MAX + 1];
|
||||
char pid_str[256];
|
||||
snprintf(pid_str, sizeof pid_str, "%ld", (long)getpid());
|
||||
|
||||
if ( gethostname( hostname, HOST_NAME_MAX + 1 ) == 0 )
|
||||
{
|
||||
hnlen = strlen( hostname );
|
||||
if ( gethostname( hostname, sizeof hostname ) != 0 )
|
||||
{
|
||||
sprint_rand_digits( hostname, HOST_NAME_MAX );
|
||||
}
|
||||
else
|
||||
{
|
||||
hnlen = sprint_rand_digits( hostname, HOST_NAME_MAX );
|
||||
hostname[hnlen] = '\0';
|
||||
}
|
||||
newname = (char *)malloc( orglen + 1 /* period */ + hnlen + 1 /* period */ +
|
||||
/* max possible pid size: 0.31 ~= log(10)2 */
|
||||
(int)(0.31 * sizeof(pid_t) * CHAR_BIT + 1)
|
||||
+ 1 /* '\0' */ );
|
||||
|
||||
if ( newname == NULL )
|
||||
{
|
||||
debug( 1, L"gen_unique_nfs_filename: %s", strerror( errno ) );
|
||||
return newname;
|
||||
}
|
||||
memcpy( newname, filename, orglen );
|
||||
newname[orglen] = '.';
|
||||
memcpy( newname + orglen + 1, hostname, hnlen );
|
||||
newname[orglen + 1 + hnlen] = '.';
|
||||
pidlen = sprint_pid_t( getpid(), newname + orglen + 1 + hnlen + 1 );
|
||||
newname[orglen + 1 + hnlen + 1 + pidlen] = '\0';
|
||||
/* debug( 1, L"gen_unique_nfs_filename returning with: newname = \"%s\"; "
|
||||
L"HOST_NAME_MAX = %d; hnlen = %d; orglen = %d; "
|
||||
L"sizeof(pid_t) = %d; maxpiddigits = %d; malloc'd size: %d",
|
||||
newname, (int)HOST_NAME_MAX, hnlen, orglen,
|
||||
(int)sizeof(pid_t),
|
||||
(int)(0.31 * sizeof(pid_t) * CHAR_BIT + 1),
|
||||
(int)(orglen + 1 + hnlen + 1 +
|
||||
(int)(0.31 * sizeof(pid_t) * CHAR_BIT + 1) + 1) ); */
|
||||
|
||||
std::string newname(filename);
|
||||
newname.push_back('.');
|
||||
newname.append(hostname);
|
||||
newname.push_back('.');
|
||||
newname.append(pid_str);
|
||||
return newname;
|
||||
}
|
||||
|
||||
@@ -348,11 +291,8 @@ static int acquire_lock_file( const char *lockfile, const int timeout, int force
|
||||
/*
|
||||
(Re)create a unique file and check that it has one only link.
|
||||
*/
|
||||
char *linkfile = gen_unique_nfs_filename( lockfile );
|
||||
if( linkfile == NULL )
|
||||
{
|
||||
goto done;
|
||||
}
|
||||
const std::string linkfile_str = gen_unique_nfs_filename( lockfile );
|
||||
const char * const linkfile = linkfile_str.c_str();
|
||||
(void)unlink( linkfile );
|
||||
/* OK to not use CLO_EXEC here because fishd is single threaded */
|
||||
if( ( fd = open( linkfile, O_CREAT|O_RDONLY, 0600 ) ) == -1 )
|
||||
@@ -439,7 +379,6 @@ static int acquire_lock_file( const char *lockfile, const int timeout, int force
|
||||
done:
|
||||
/* The linkfile is not needed once the lockfile has been created */
|
||||
(void)unlink( linkfile );
|
||||
free( linkfile );
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -452,7 +391,7 @@ done:
|
||||
*/
|
||||
static char *acquire_socket_lock( const char *sock_name )
|
||||
{
|
||||
int len = strlen( sock_name );
|
||||
size_t len = strlen( sock_name );
|
||||
char *lockfile = (char *)malloc( len + strlen( LOCKPOSTFIX ) + 1 );
|
||||
|
||||
if( lockfile == NULL )
|
||||
@@ -557,7 +496,7 @@ unlock:
|
||||
/**
|
||||
Event handler. Broadcasts updates to all clients.
|
||||
*/
|
||||
static void broadcast( int type, const wchar_t *key, const wchar_t *val )
|
||||
static void broadcast( fish_message_type_t type, const wchar_t *key, const wchar_t *val )
|
||||
{
|
||||
connection_t *c;
|
||||
message_t *msg;
|
||||
@@ -601,6 +540,9 @@ static void daemonize()
|
||||
|
||||
case 0:
|
||||
{
|
||||
/* Ordinarily there's very limited things we will do after fork, due to multithreading. But fishd is safe because it's single threaded. So don't die in is_forked_child. */
|
||||
setup_fork_guards();
|
||||
|
||||
/*
|
||||
Make fishd ignore the HUP signal.
|
||||
*/
|
||||
@@ -618,6 +560,7 @@ static void daemonize()
|
||||
act.sa_handler=&handle_term;
|
||||
sigaction( SIGTERM, &act, 0);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
default:
|
||||
|
||||
@@ -192,7 +192,7 @@ void function_add( const function_data_t &data, const parser_t &parser )
|
||||
|
||||
/* Create and store a new function */
|
||||
const wchar_t *filename = reader_current_filename();
|
||||
int def_offset = parse_util_lineno( parser.get_buffer(), parser.current_block->tok_pos )-1;
|
||||
int def_offset = parser.line_number_of_character_at_offset(parser.current_block->tok_pos) - 1;
|
||||
const function_map_t::value_type new_pair(data.name, function_info_t(data, filename, def_offset, is_autoload));
|
||||
loaded_functions.insert(new_pair);
|
||||
|
||||
@@ -212,7 +212,7 @@ int function_exists( const wcstring &cmd )
|
||||
return loaded_functions.find(cmd) != loaded_functions.end();
|
||||
}
|
||||
|
||||
int function_exists_no_autoload( const wcstring &cmd, const env_vars &vars )
|
||||
int function_exists_no_autoload( const wcstring &cmd, const env_vars_snapshot_t &vars )
|
||||
{
|
||||
if( parser_keywords_is_reserved(cmd) )
|
||||
return 0;
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
#include "event.h"
|
||||
|
||||
class parser_t;
|
||||
class env_vars;
|
||||
class env_vars_snapshot_t;
|
||||
|
||||
/**
|
||||
Structure describing a function. This is used by the parser to
|
||||
@@ -124,7 +124,7 @@ int function_exists( const wcstring &name );
|
||||
/**
|
||||
Returns true if the function with the name name exists, without triggering autoload.
|
||||
*/
|
||||
int function_exists_no_autoload( const wcstring &name, const env_vars &vars );
|
||||
int function_exists_no_autoload( const wcstring &name, const env_vars_snapshot_t &vars );
|
||||
|
||||
/**
|
||||
Returns all function names.
|
||||
|
||||
239
highlight.cpp
239
highlight.cpp
@@ -33,15 +33,14 @@
|
||||
#include "output.h"
|
||||
#include "wildcard.h"
|
||||
#include "path.h"
|
||||
#include "history.h"
|
||||
|
||||
/**
|
||||
Number of elements in the highlight_var array
|
||||
*/
|
||||
#define VAR_COUNT ( sizeof(highlight_var)/sizeof(wchar_t *) )
|
||||
|
||||
static void highlight_universal_internal( const wcstring &buff,
|
||||
std::vector<int> &color,
|
||||
int pos );
|
||||
static void highlight_universal_internal( const wcstring &buff, std::vector<int> &color, size_t pos );
|
||||
|
||||
/**
|
||||
The environment variables used to specify the color of different tokens.
|
||||
@@ -103,29 +102,51 @@ static wcstring apply_working_directory(const wcstring &path, const wcstring &wo
|
||||
}
|
||||
}
|
||||
|
||||
/* Determine if the filesystem containing the given fd is case insensitive. */
|
||||
typedef std::map<wcstring, bool> case_sensitivity_cache_t;
|
||||
bool fs_is_case_insensitive(const wcstring &path, int fd, case_sensitivity_cache_t &case_sensitivity_cache)
|
||||
{
|
||||
/* If _PC_CASE_SENSITIVE is not defined, assume case sensitive */
|
||||
bool result = false;
|
||||
#ifdef _PC_CASE_SENSITIVE
|
||||
/* Try the cache first */
|
||||
case_sensitivity_cache_t::iterator cache = case_sensitivity_cache.find(path);
|
||||
if (cache != case_sensitivity_cache.end()) {
|
||||
/* Use the cached value */
|
||||
result = cache->second;
|
||||
} else {
|
||||
/* Ask the system. A -1 value means error (so assume case sensitive), a 1 value means case sensitive, and a 0 value means case insensitive */
|
||||
long ret = fpathconf(fd, _PC_CASE_SENSITIVE);
|
||||
result = (ret == 0);
|
||||
case_sensitivity_cache[path] = result;
|
||||
}
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Tests whether the specified string cpath is the prefix of anything we could cd to. directories is a list of possible parent directories (typically either the working directory, or the cdpath). This does I/O!
|
||||
|
||||
We expect the path to already be unescaped.
|
||||
*/
|
||||
bool is_potential_path(const wcstring &const_path, const wcstring_list_t &directories, bool require_dir, wcstring *out_path)
|
||||
bool is_potential_path(const wcstring &const_path, const wcstring_list_t &directories, path_flags_t flags, wcstring *out_path)
|
||||
{
|
||||
ASSERT_IS_BACKGROUND_THREAD();
|
||||
|
||||
const wchar_t *unescaped, *in;
|
||||
const bool require_dir = !! (flags & PATH_REQUIRE_DIR);
|
||||
wcstring clean_path;
|
||||
int has_magic = 0;
|
||||
bool result = false;
|
||||
|
||||
wcstring path(const_path);
|
||||
expand_tilde(path);
|
||||
if (! unescape_string(path, 1))
|
||||
return false;
|
||||
|
||||
unescaped = path.c_str();
|
||||
|
||||
if (flags & PATH_EXPAND_TILDE)
|
||||
expand_tilde(path);
|
||||
|
||||
// debug( 1, L"%ls -> %ls ->%ls", path, tilde, unescaped );
|
||||
|
||||
for( in = unescaped; *in; in++ )
|
||||
for( size_t i=0; i < path.size(); i++)
|
||||
{
|
||||
switch( *in )
|
||||
wchar_t c = path.at(i);
|
||||
switch( c )
|
||||
{
|
||||
case PROCESS_EXPAND:
|
||||
case VARIABLE_EXPAND:
|
||||
@@ -148,7 +169,7 @@ bool is_potential_path(const wcstring &const_path, const wcstring_list_t &direct
|
||||
|
||||
default:
|
||||
{
|
||||
clean_path.append(in, 1);
|
||||
clean_path.push_back(c);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -161,6 +182,9 @@ bool is_potential_path(const wcstring &const_path, const wcstring_list_t &direct
|
||||
/* Don't test the same path multiple times, which can happen if the path is absolute and the CDPATH contains multiple entries */
|
||||
std::set<wcstring> checked_paths;
|
||||
|
||||
/* Keep a cache of which paths / filesystems are case sensitive */
|
||||
case_sensitivity_cache_t case_sensitivity_cache;
|
||||
|
||||
for (size_t wd_idx = 0; wd_idx < directories.size() && ! result; wd_idx++) {
|
||||
const wcstring &wd = directories.at(wd_idx);
|
||||
|
||||
@@ -200,12 +224,23 @@ bool is_potential_path(const wcstring &const_path, const wcstring_list_t &direct
|
||||
// We opened the dir_name; look for a string where the base name prefixes it
|
||||
wcstring ent;
|
||||
|
||||
// Check if we're case insensitive
|
||||
bool case_insensitive = fs_is_case_insensitive(dir_name, dirfd(dir), case_sensitivity_cache);
|
||||
|
||||
// Don't ask for the is_dir value unless we care, because it can cause extra filesystem acces */
|
||||
bool is_dir = false;
|
||||
while (wreaddir_resolving(dir, dir_name, ent, require_dir ? &is_dir : NULL))
|
||||
{
|
||||
// TODO: support doing the right thing on case-insensitive filesystems like HFS+
|
||||
if (string_prefixes_string(base_name, ent) && (! require_dir || is_dir))
|
||||
{
|
||||
|
||||
/* Determine which function to call to check for prefixes */
|
||||
bool (*prefix_func)(const wcstring &, const wcstring &);
|
||||
if (case_insensitive) {
|
||||
prefix_func = string_prefixes_string_case_insensitive;
|
||||
} else {
|
||||
prefix_func = string_prefixes_string;
|
||||
}
|
||||
|
||||
if (prefix_func(base_name, ent) && (! require_dir || is_dir))
|
||||
{
|
||||
result = true;
|
||||
if (out_path) {
|
||||
@@ -213,7 +248,9 @@ bool is_potential_path(const wcstring &const_path, const wcstring_list_t &direct
|
||||
|
||||
out_path->clear();
|
||||
const wcstring path_base = wdirname(const_path);
|
||||
if (string_prefixes_string(path_base, const_path)) {
|
||||
|
||||
|
||||
if (prefix_func(path_base, const_path)) {
|
||||
out_path->append(path_base);
|
||||
if (! string_suffixes_string(L"/", *out_path))
|
||||
out_path->push_back(L'/');
|
||||
@@ -235,8 +272,9 @@ bool is_potential_path(const wcstring &const_path, const wcstring_list_t &direct
|
||||
}
|
||||
|
||||
|
||||
/* Given a string, return whether it prefixes a path that we could cd into. Return that path in out_path */
|
||||
static bool is_potential_cd_path(const wcstring &path, const wcstring &working_directory, wcstring *out_path) {
|
||||
/* Given a string, return whether it prefixes a path that we could cd into. Return that path in out_path. Expects path to be unescaped. */
|
||||
static bool is_potential_cd_path(const wcstring &path, const wcstring &working_directory, path_flags_t flags, wcstring *out_path)
|
||||
{
|
||||
wcstring_list_t directories;
|
||||
|
||||
if (string_prefixes_string(L"./", path)) {
|
||||
@@ -259,7 +297,7 @@ static bool is_potential_cd_path(const wcstring &path, const wcstring &working_d
|
||||
}
|
||||
|
||||
/* Call is_potential_path with all of these directories */
|
||||
bool result = is_potential_path(path, directories, true /* require_dir */, out_path);
|
||||
bool result = is_potential_path(path, directories, flags | PATH_REQUIRE_DIR, out_path);
|
||||
#if 0
|
||||
if (out_path) {
|
||||
printf("%ls -> %ls\n", path.c_str(), out_path->c_str());
|
||||
@@ -270,15 +308,14 @@ static bool is_potential_cd_path(const wcstring &path, const wcstring &working_d
|
||||
|
||||
rgb_color_t highlight_get_color( int highlight, bool is_background )
|
||||
{
|
||||
size_t i;
|
||||
int idx=0;
|
||||
size_t idx=0;
|
||||
rgb_color_t result;
|
||||
|
||||
if( highlight < 0 )
|
||||
return rgb_color_t::normal();
|
||||
if( highlight > (1<<VAR_COUNT) )
|
||||
return rgb_color_t::normal();
|
||||
for( i=0; i<VAR_COUNT; i++ )
|
||||
for( size_t i=0; i<VAR_COUNT; i++ )
|
||||
{
|
||||
if( highlight & (1<<i ))
|
||||
{
|
||||
@@ -320,7 +357,7 @@ rgb_color_t highlight_get_color( int highlight, bool is_background )
|
||||
/**
|
||||
Highlight operators (such as $, ~, %, as well as escaped characters.
|
||||
*/
|
||||
static void highlight_param( const wcstring &buffstr, std::vector<int> &colors, int pos, wcstring_list_t *error )
|
||||
static void highlight_param( const wcstring &buffstr, std::vector<int> &colors, wcstring_list_t *error )
|
||||
{
|
||||
const wchar_t * const buff = buffstr.c_str();
|
||||
enum {e_unquoted, e_single_quoted, e_double_quoted} mode = e_unquoted;
|
||||
@@ -417,7 +454,7 @@ static void highlight_param( const wcstring &buffstr, std::vector<int> &colors,
|
||||
|
||||
for( i=0; i<chars; i++ )
|
||||
{
|
||||
int d = convert_digit( buff[++in_pos],base);
|
||||
long d = convert_digit( buff[++in_pos],base);
|
||||
|
||||
if( d < 0 )
|
||||
{
|
||||
@@ -527,7 +564,7 @@ static void highlight_param( const wcstring &buffstr, std::vector<int> &colors,
|
||||
{
|
||||
if( c == L'\\' )
|
||||
{
|
||||
int start_pos = in_pos;
|
||||
size_t start_pos = in_pos;
|
||||
switch( buff[++in_pos] )
|
||||
{
|
||||
case '\\':
|
||||
@@ -571,7 +608,7 @@ static void highlight_param( const wcstring &buffstr, std::vector<int> &colors,
|
||||
|
||||
case '\\':
|
||||
{
|
||||
int start_pos = in_pos;
|
||||
size_t start_pos = in_pos;
|
||||
switch( buff[++in_pos] )
|
||||
{
|
||||
case L'\0':
|
||||
@@ -632,7 +669,7 @@ static bool autosuggest_parse_command(const wcstring &str, wcstring *out_command
|
||||
|
||||
bool had_cmd = false;
|
||||
tokenizer tok;
|
||||
for (tok_init( &tok, str.c_str(), TOK_SQUASH_ERRORS); tok_has_next(&tok); tok_next(&tok))
|
||||
for (tok_init( &tok, str.c_str(), TOK_ACCEPT_UNFINISHED | TOK_SQUASH_ERRORS); tok_has_next(&tok); tok_next(&tok))
|
||||
{
|
||||
int last_type = tok_last_type(&tok);
|
||||
|
||||
@@ -642,7 +679,7 @@ static bool autosuggest_parse_command(const wcstring &str, wcstring *out_command
|
||||
{
|
||||
if( had_cmd )
|
||||
{
|
||||
/* Parameter to the command */
|
||||
/* Parameter to the command. We store these escaped. */
|
||||
args.push_back(tok_last(&tok));
|
||||
arg_pos = tok_get_pos(&tok);
|
||||
}
|
||||
@@ -737,6 +774,7 @@ static bool autosuggest_parse_command(const wcstring &str, wcstring *out_command
|
||||
}
|
||||
|
||||
|
||||
/* We have to return an escaped string here */
|
||||
bool autosuggest_suggest_special(const wcstring &str, const wcstring &working_directory, wcstring &outSuggestion) {
|
||||
if (str.empty())
|
||||
return false;
|
||||
@@ -753,18 +791,35 @@ bool autosuggest_suggest_special(const wcstring &str, const wcstring &working_di
|
||||
bool result = false;
|
||||
if (parsed_command == L"cd" && ! parsed_arguments.empty()) {
|
||||
/* We can possibly handle this specially */
|
||||
wcstring dir = parsed_arguments.back();
|
||||
const wcstring escaped_dir = parsed_arguments.back();
|
||||
wcstring suggested_path;
|
||||
|
||||
/* We always return true because we recognized the command. This prevents us from falling back to dumber algorithms; for example we won't suggest a non-directory for the cd command. */
|
||||
result = true;
|
||||
outSuggestion.clear();
|
||||
|
||||
/* Unescape the parameter */
|
||||
wcstring unescaped_dir = escaped_dir;
|
||||
bool unescaped = unescape_string(unescaped_dir, UNESCAPE_INCOMPLETE);
|
||||
|
||||
/* Determine the quote type we got from the input directory. */
|
||||
wchar_t quote = L'\0';
|
||||
parse_util_get_parameter_info(escaped_dir, 0, "e, NULL, NULL);
|
||||
|
||||
/* Big hack to avoid expanding a tilde inside quotes */
|
||||
path_flags_t path_flags = (quote == L'\0') ? PATH_EXPAND_TILDE : 0;
|
||||
if (unescaped && is_potential_cd_path(unescaped_dir, working_directory, path_flags, &suggested_path))
|
||||
{
|
||||
|
||||
/* Note: this looks really wrong for strings that have an "unescapable" character in them, e.g. a \t, because parse_util_escape_string_with_quote will insert that character */
|
||||
wcstring escaped_suggested_path = parse_util_escape_string_with_quote(suggested_path, quote);
|
||||
|
||||
if (is_potential_cd_path(dir, working_directory, &suggested_path)) {
|
||||
/* Success */
|
||||
/* Return it */
|
||||
outSuggestion = str;
|
||||
outSuggestion.erase(parsed_last_arg_pos);
|
||||
outSuggestion.append(suggested_path);
|
||||
if (quote != L'\0') outSuggestion.push_back(quote);
|
||||
outSuggestion.append(escaped_suggested_path);
|
||||
if (quote != L'\0') outSuggestion.push_back(quote);
|
||||
}
|
||||
} else {
|
||||
/* Either an error or some other command, so we don't handle it specially */
|
||||
@@ -772,17 +827,16 @@ bool autosuggest_suggest_special(const wcstring &str, const wcstring &working_di
|
||||
return result;
|
||||
}
|
||||
|
||||
bool autosuggest_special_validate_from_history(const wcstring &str, const wcstring &working_directory, bool *outSuggestionOK) {
|
||||
bool autosuggest_validate_from_history(const history_item_t &item, file_detection_context_t &detector, const wcstring &working_directory, const env_vars_snapshot_t &vars) {
|
||||
ASSERT_IS_BACKGROUND_THREAD();
|
||||
assert(outSuggestionOK != NULL);
|
||||
|
||||
|
||||
bool handled = false, suggestionOK = false;
|
||||
|
||||
/* Parse the string */
|
||||
wcstring parsed_command;
|
||||
wcstring_list_t parsed_arguments;
|
||||
int parsed_last_arg_pos = -1;
|
||||
if (! autosuggest_parse_command(str, &parsed_command, &parsed_arguments, &parsed_last_arg_pos))
|
||||
if (! autosuggest_parse_command(item.str(), &parsed_command, &parsed_arguments, &parsed_last_arg_pos))
|
||||
return false;
|
||||
|
||||
if (parsed_command == L"cd" && ! parsed_arguments.empty()) {
|
||||
@@ -795,8 +849,9 @@ bool autosuggest_special_validate_from_history(const wcstring &str, const wcstri
|
||||
if (is_help) {
|
||||
suggestionOK = false;
|
||||
} else {
|
||||
wchar_t *path = path_allocate_cdpath(dir, working_directory.c_str());
|
||||
if (path == NULL) {
|
||||
wcstring path;
|
||||
bool can_cd = path_get_cdpath(dir, &path, working_directory.c_str(), vars);
|
||||
if (! can_cd) {
|
||||
suggestionOK = false;
|
||||
} else if (paths_are_same_file(working_directory, path)) {
|
||||
/* Don't suggest the working directory as the path! */
|
||||
@@ -804,24 +859,45 @@ bool autosuggest_special_validate_from_history(const wcstring &str, const wcstri
|
||||
} else {
|
||||
suggestionOK = true;
|
||||
}
|
||||
free(path);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* Either an error or some other command, so we don't handle it specially */
|
||||
}
|
||||
|
||||
/* If not handled specially, handle it here */
|
||||
if (! handled) {
|
||||
bool cmd_ok = false;
|
||||
|
||||
if (path_get_path(parsed_command, NULL))
|
||||
{
|
||||
cmd_ok = true;
|
||||
}
|
||||
else if (builtin_exists(parsed_command) || function_exists_no_autoload(parsed_command, vars))
|
||||
{
|
||||
cmd_ok = true;
|
||||
}
|
||||
|
||||
if (cmd_ok) {
|
||||
const path_list_t &paths = item.get_required_paths();
|
||||
if (paths.empty()) {
|
||||
suggestionOK= true;
|
||||
}
|
||||
else {
|
||||
detector.potential_paths = paths;
|
||||
suggestionOK = detector.paths_are_valid(paths);
|
||||
}
|
||||
}
|
||||
}
|
||||
*outSuggestionOK = suggestionOK;
|
||||
return handled;
|
||||
|
||||
return suggestionOK;
|
||||
}
|
||||
|
||||
// This function does I/O
|
||||
static void tokenize( const wchar_t * const buff, std::vector<int> &color, const int pos, wcstring_list_t *error, const wcstring &working_directory, const env_vars &vars) {
|
||||
static void tokenize( const wchar_t * const buff, std::vector<int> &color, const size_t pos, wcstring_list_t *error, const wcstring &working_directory, const env_vars_snapshot_t &vars) {
|
||||
ASSERT_IS_BACKGROUND_THREAD();
|
||||
|
||||
wcstring cmd;
|
||||
wcstring cmd;
|
||||
int had_cmd=0;
|
||||
wcstring last_cmd;
|
||||
int len;
|
||||
|
||||
int accept_switches = 1;
|
||||
|
||||
@@ -831,10 +907,8 @@ static void tokenize( const wchar_t * const buff, std::vector<int> &color, const
|
||||
|
||||
CHECK( buff, );
|
||||
|
||||
len = wcslen(buff);
|
||||
|
||||
if( !len )
|
||||
return;
|
||||
if (buff[0] == L'\0')
|
||||
return;
|
||||
|
||||
std::fill(color.begin(), color.end(), -1);
|
||||
|
||||
@@ -863,7 +937,7 @@ static void tokenize( const wchar_t * const buff, std::vector<int> &color, const
|
||||
}
|
||||
else if( accept_switches )
|
||||
{
|
||||
if( complete_is_valid_option( last_cmd.c_str(), param, error, false /* no autoload */ ) )
|
||||
if( complete_is_valid_option( last_cmd, param, error, false /* no autoload */ ) )
|
||||
color.at(tok_get_pos( &tok )) = HIGHLIGHT_PARAM;
|
||||
else
|
||||
color.at(tok_get_pos( &tok )) = HIGHLIGHT_ERROR;
|
||||
@@ -884,7 +958,7 @@ static void tokenize( const wchar_t * const buff, std::vector<int> &color, const
|
||||
if (expand_one(dir, EXPAND_SKIP_CMDSUBST))
|
||||
{
|
||||
int is_help = string_prefixes_string(dir, L"--help") || string_prefixes_string(dir, L"-h");
|
||||
if( !is_help && ! is_potential_cd_path(dir, working_directory, NULL))
|
||||
if( !is_help && ! is_potential_cd_path(dir, working_directory, PATH_EXPAND_TILDE, NULL))
|
||||
{
|
||||
color.at(tok_get_pos( &tok )) = HIGHLIGHT_ERROR;
|
||||
}
|
||||
@@ -893,12 +967,12 @@ static void tokenize( const wchar_t * const buff, std::vector<int> &color, const
|
||||
|
||||
/* Highlight the parameter. highlight_param wants to write one more color than we have characters (hysterical raisins) so allocate one more in the vector. But don't copy it back. */
|
||||
const wcstring param_str = param;
|
||||
int tok_pos = tok_get_pos(&tok);
|
||||
size_t tok_pos = tok_get_pos(&tok);
|
||||
|
||||
std::vector<int>::const_iterator where = color.begin() + tok_pos;
|
||||
std::vector<int> subcolors(where, where + param_str.size());
|
||||
subcolors.push_back(-1);
|
||||
highlight_param(param_str, subcolors, pos-tok_pos, error);
|
||||
subcolors.push_back(-1);
|
||||
highlight_param(param_str, subcolors, error);
|
||||
|
||||
/* Copy the subcolors back into our colors array */
|
||||
std::copy(subcolors.begin(), subcolors.begin() + param_str.size(), color.begin() + tok_pos);
|
||||
@@ -909,7 +983,7 @@ static void tokenize( const wchar_t * const buff, std::vector<int> &color, const
|
||||
Command. First check that the command actually exists.
|
||||
*/
|
||||
cmd = tok_last( &tok );
|
||||
bool expanded = expand_one(cmd, EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_VARIABLES);
|
||||
bool expanded = expand_one(cmd, EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_VARIABLES | EXPAND_SKIP_JOBS);
|
||||
if (! expanded || has_expand_reserved(cmd.c_str()))
|
||||
{
|
||||
color.at(tok_get_pos( &tok )) = HIGHLIGHT_ERROR;
|
||||
@@ -999,15 +1073,14 @@ static void tokenize( const wchar_t * const buff, std::vector<int> &color, const
|
||||
*/
|
||||
if (! is_cmd && use_command )
|
||||
{
|
||||
wcstring tmp;
|
||||
is_cmd = path_get_path_string( cmd, tmp, vars );
|
||||
is_cmd = path_get_path( cmd, NULL, vars );
|
||||
}
|
||||
|
||||
/* Maybe it is a path for a implicit cd command. */
|
||||
if (! is_cmd)
|
||||
{
|
||||
if (use_builtin || (use_function && function_exists_no_autoload( L"cd", vars)))
|
||||
is_cmd = path_can_be_implicit_cd(cmd, NULL, working_directory.c_str());
|
||||
is_cmd = path_can_be_implicit_cd(cmd, NULL, working_directory.c_str(), vars);
|
||||
}
|
||||
|
||||
if( is_cmd )
|
||||
@@ -1189,14 +1262,14 @@ static void tokenize( const wchar_t * const buff, std::vector<int> &color, const
|
||||
|
||||
|
||||
// PCA This function does I/O, (calls is_potential_path, path_get_path, maybe others) and so ought to only run on a background thread
|
||||
void highlight_shell( const wcstring &buff, std::vector<int> &color, int pos, wcstring_list_t *error, const env_vars &vars )
|
||||
void highlight_shell( const wcstring &buff, std::vector<int> &color, size_t pos, wcstring_list_t *error, const env_vars_snapshot_t &vars )
|
||||
{
|
||||
ASSERT_IS_BACKGROUND_THREAD();
|
||||
|
||||
const size_t length = buff.size();
|
||||
assert(buff.size() == color.size());
|
||||
|
||||
|
||||
|
||||
if( length == 0 )
|
||||
return;
|
||||
|
||||
@@ -1270,7 +1343,7 @@ void highlight_shell( const wcstring &buff, std::vector<int> &color, int pos, wc
|
||||
are the current token.
|
||||
For reasons that I don't yet understand, it's required that pos be allowed to be length (e.g. when backspacing).
|
||||
*/
|
||||
if( pos >= 0 && (size_t)pos <= length )
|
||||
if( pos <= length )
|
||||
{
|
||||
|
||||
const wchar_t *cbuff = buff.c_str();
|
||||
@@ -1278,18 +1351,24 @@ void highlight_shell( const wcstring &buff, std::vector<int> &color, int pos, wc
|
||||
parse_util_token_extent( cbuff, pos, &tok_begin, &tok_end, 0, 0 );
|
||||
if( tok_begin && tok_end )
|
||||
{
|
||||
const wcstring token(tok_begin, tok_end-tok_begin);
|
||||
wcstring token(tok_begin, tok_end-tok_begin);
|
||||
const wcstring_list_t working_directory_list(1, working_directory);
|
||||
if (is_potential_path(token, working_directory_list))
|
||||
{
|
||||
for( ptrdiff_t i=tok_begin-cbuff; i < (tok_end-cbuff); i++ )
|
||||
{
|
||||
// Don't color HIGHLIGHT_ERROR because it looks dorky. For example, trying to cd into a non-directory would show an underline and also red.
|
||||
if (! (color.at(i) & HIGHLIGHT_ERROR)) {
|
||||
color.at(i) |= HIGHLIGHT_VALID_PATH;
|
||||
if (unescape_string(token, 1))
|
||||
{
|
||||
/* Big hack: is_potential_path expects a tilde, but unescape_string gives us HOME_DIRECTORY. Put it back. */
|
||||
if (! token.empty() && token.at(0) == HOME_DIRECTORY)
|
||||
token.at(0) = L'~';
|
||||
if (is_potential_path(token, working_directory_list, PATH_EXPAND_TILDE))
|
||||
{
|
||||
for( ptrdiff_t i=tok_begin-cbuff; i < (tok_end-cbuff); i++ )
|
||||
{
|
||||
// Don't color HIGHLIGHT_ERROR because it looks dorky. For example, trying to cd into a non-directory would show an underline and also red.
|
||||
if (! (color.at(i) & HIGHLIGHT_ERROR)) {
|
||||
color.at(i) |= HIGHLIGHT_VALID_PATH;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1313,12 +1392,10 @@ void highlight_shell( const wcstring &buff, std::vector<int> &color, int pos, wc
|
||||
/**
|
||||
Perform quote and parenthesis highlighting on the specified string.
|
||||
*/
|
||||
static void highlight_universal_internal( const wcstring &buffstr,
|
||||
std::vector<int> &color,
|
||||
int pos )
|
||||
static void highlight_universal_internal( const wcstring &buffstr, std::vector<int> &color, size_t pos )
|
||||
{
|
||||
assert(buffstr.size() == color.size());
|
||||
if( (pos >= 0) && ((size_t)pos < buffstr.size()) )
|
||||
if( pos < buffstr.size() )
|
||||
{
|
||||
|
||||
/*
|
||||
@@ -1348,7 +1425,7 @@ static void highlight_universal_internal( const wcstring &buffstr,
|
||||
if( level == 0 )
|
||||
{
|
||||
level++;
|
||||
lst.push_back((long)(str-buff));
|
||||
lst.push_back(str-buff);
|
||||
prev_q = *str;
|
||||
}
|
||||
else
|
||||
@@ -1372,7 +1449,7 @@ static void highlight_universal_internal( const wcstring &buffstr,
|
||||
else
|
||||
{
|
||||
level++;
|
||||
lst.push_back((long)(str-buff));
|
||||
lst.push_back(str-buff);
|
||||
prev_q = *str;
|
||||
}
|
||||
}
|
||||
@@ -1422,7 +1499,7 @@ static void highlight_universal_internal( const wcstring &buffstr,
|
||||
}
|
||||
}
|
||||
|
||||
void highlight_universal( const wcstring &buff, std::vector<int> &color, int pos, wcstring_list_t *error, const env_vars &vars )
|
||||
void highlight_universal( const wcstring &buff, std::vector<int> &color, size_t pos, wcstring_list_t *error, const env_vars_snapshot_t &vars )
|
||||
{
|
||||
assert(buff.size() == color.size());
|
||||
std::fill(color.begin(), color.end(), 0);
|
||||
|
||||
21
highlight.h
21
highlight.h
@@ -70,6 +70,9 @@
|
||||
*/
|
||||
#define HIGHLIGHT_AUTOSUGGESTION 0x2000
|
||||
|
||||
class history_item_t;
|
||||
struct file_detection_context_t;
|
||||
|
||||
/**
|
||||
Perform syntax highlighting for the shell commands in buff. The result is
|
||||
stored in the color array as a color_code from the HIGHLIGHT_ enum
|
||||
@@ -80,7 +83,7 @@
|
||||
\param pos the cursor position. Used for quote matching, etc.
|
||||
\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, int pos, wcstring_list_t *error, const env_vars &vars );
|
||||
void highlight_shell( 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
|
||||
@@ -92,7 +95,7 @@ void highlight_shell( const wcstring &buffstr, std::vector<int> &color, int pos,
|
||||
\param pos the cursor position. Used for quote matching, etc.
|
||||
\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_universal( const wcstring &buffstr, std::vector<int> &color, int pos, wcstring_list_t *error, const env_vars &vars );
|
||||
void highlight_universal( const wcstring &buffstr, std::vector<int> &color, size_t pos, wcstring_list_t *error, const env_vars_snapshot_t &vars );
|
||||
|
||||
/**
|
||||
Translate from HIGHLIGHT_* to FISH_COLOR_* according to environment
|
||||
@@ -109,9 +112,9 @@ rgb_color_t highlight_get_color( int highlight, bool is_background );
|
||||
/** Given a command 'str' from the history, try to determine whether we ought to suggest it by specially recognizing the command.
|
||||
Returns true if we validated the command. If so, returns by reference whether the suggestion is valid or not.
|
||||
*/
|
||||
bool autosuggest_special_validate_from_history(const wcstring &str, const wcstring &working_directory, bool *outSuggestionOK);
|
||||
bool autosuggest_validate_from_history(const history_item_t &item, file_detection_context_t &detector, const wcstring &working_directory, const env_vars_snapshot_t &vars);
|
||||
|
||||
/** Given the command line contents 'str', return via reference a suggestion by specially recognizing the command. Returns true if we recognized the command (even if we couldn't think of a suggestion for it).
|
||||
/** Given the command line contents 'str', return via reference a suggestion by specially recognizing the command. The suggestion is escaped. Returns true if we recognized the command (even if we couldn't think of a suggestion for it).
|
||||
*/
|
||||
bool autosuggest_suggest_special(const wcstring &str, const wcstring &working_directory, wcstring &outString);
|
||||
|
||||
@@ -119,7 +122,15 @@ bool autosuggest_suggest_special(const wcstring &str, const wcstring &working_di
|
||||
|
||||
This is used only internally to this file, and is exposed only for testing.
|
||||
*/
|
||||
bool is_potential_path(const wcstring &const_path, const wcstring_list_t &directories, bool require_dir = false, wcstring *out_path = NULL);
|
||||
enum {
|
||||
/* The path must be to a directory */
|
||||
PATH_REQUIRE_DIR = 1 << 0,
|
||||
|
||||
/* Expand any leading tilde in the path */
|
||||
PATH_EXPAND_TILDE = 1 << 1
|
||||
};
|
||||
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);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
345
history.cpp
345
history.cpp
@@ -91,7 +91,6 @@ class history_lru_node_t : public lru_node_t {
|
||||
required_paths(item.required_paths)
|
||||
{}
|
||||
|
||||
bool write_to_file(FILE *f) const;
|
||||
bool write_yaml_to_file(FILE *f) const;
|
||||
};
|
||||
|
||||
@@ -264,7 +263,7 @@ static const char *next_line(const char *start, size_t length) {
|
||||
// Pass a pointer to a cursor size_t, initially 0
|
||||
// If custoff_timestamp is nonzero, skip items created at or after that timestamp
|
||||
// Returns (size_t)(-1) when done
|
||||
static size_t offset_of_next_item(const char *begin, size_t mmap_length, size_t *inout_cursor, time_t cutoff_timestamp)
|
||||
static size_t offset_of_next_item_fish_2_0(const char *begin, size_t mmap_length, size_t *inout_cursor, time_t cutoff_timestamp)
|
||||
{
|
||||
size_t cursor = *inout_cursor;
|
||||
size_t result = (size_t)(-1);
|
||||
@@ -334,6 +333,77 @@ static size_t offset_of_next_item(const char *begin, size_t mmap_length, size_t
|
||||
}
|
||||
|
||||
|
||||
// Same as offset_of_next_item_fish_2_0, but for fish 1.x (pre fishfish)
|
||||
// Adapted from history_populate_from_mmap in history.c
|
||||
static size_t offset_of_next_item_fish_1_x(const char *begin, size_t mmap_length, size_t *inout_cursor, time_t cutoff_timestamp) {
|
||||
if (mmap_length == 0 || *inout_cursor >= mmap_length)
|
||||
return (size_t)(-1);
|
||||
|
||||
const char *end = begin + mmap_length;
|
||||
const char *pos;
|
||||
|
||||
bool ignore_newline = false;
|
||||
bool do_push = true;
|
||||
bool all_done = false;
|
||||
|
||||
size_t result = *inout_cursor;
|
||||
for( pos = begin + *inout_cursor; pos < end && ! all_done; pos++ )
|
||||
{
|
||||
|
||||
if( do_push )
|
||||
{
|
||||
ignore_newline = (*pos == '#');
|
||||
do_push = false;
|
||||
}
|
||||
|
||||
switch( *pos )
|
||||
{
|
||||
case '\\':
|
||||
{
|
||||
pos++;
|
||||
break;
|
||||
}
|
||||
|
||||
case '\n':
|
||||
{
|
||||
if( ignore_newline )
|
||||
{
|
||||
ignore_newline = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Note: pos will be left pointing just after this newline, because of the ++ in the loop */
|
||||
all_done = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
*inout_cursor = (pos - begin);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Returns the offset of the next item based on the given history type, or -1
|
||||
static size_t offset_of_next_item(const char *begin, size_t mmap_length, history_file_type_t mmap_type, size_t *inout_cursor, time_t cutoff_timestamp) {
|
||||
size_t result;
|
||||
switch (mmap_type) {
|
||||
case history_type_fish_2_0:
|
||||
result = offset_of_next_item_fish_2_0(begin, mmap_length, inout_cursor, cutoff_timestamp);
|
||||
break;
|
||||
|
||||
case history_type_fish_1_x:
|
||||
result = offset_of_next_item_fish_1_x(begin, mmap_length, inout_cursor, cutoff_timestamp);
|
||||
break;
|
||||
|
||||
default:
|
||||
case history_type_unknown:
|
||||
// Oh well
|
||||
result = (size_t)(-1);
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
history_t & history_t::history_with_name(const wcstring &name) {
|
||||
/* Note that histories are currently never deleted, so we can return a reference to them without using something like shared_ptr */
|
||||
scoped_lock locker(hist_lock);
|
||||
@@ -395,19 +465,18 @@ void history_t::add(const wcstring &str, const path_list_t &valid_paths)
|
||||
|
||||
void history_t::remove(const wcstring &str)
|
||||
{
|
||||
history_item_t item_to_delete(str);
|
||||
deleted_items.push_back(item_to_delete);
|
||||
|
||||
for (std::vector<history_item_t>::iterator iter = new_items.begin(); iter != new_items.end(); ++iter)
|
||||
/* Add to our list of deleted items */
|
||||
deleted_items.insert(str);
|
||||
|
||||
/* Remove from our list of new items */
|
||||
for (std::vector<history_item_t>::iterator iter = new_items.begin(); iter != new_items.end();)
|
||||
{
|
||||
if (iter->match_contents(item_to_delete))
|
||||
{
|
||||
new_items.erase(iter);
|
||||
break;
|
||||
if (iter->str() == str) {
|
||||
iter = new_items.erase(iter);
|
||||
} else {
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
|
||||
save();
|
||||
}
|
||||
|
||||
void history_t::get_string_representation(wcstring &result, const wcstring &separator)
|
||||
@@ -426,9 +495,9 @@ void history_t::get_string_representation(wcstring &result, const wcstring &sepa
|
||||
|
||||
/* Append old items */
|
||||
load_old_if_needed();
|
||||
for (std::deque<size_t>::const_reverse_iterator iter = old_item_offsets.rbegin(); iter != old_item_offsets.rend(); ++iter) {
|
||||
for (std::vector<size_t>::const_reverse_iterator iter = old_item_offsets.rbegin(); iter != old_item_offsets.rend(); ++iter) {
|
||||
size_t offset = *iter;
|
||||
const history_item_t item = history_t::decode_item(mmap_start + offset, mmap_length - offset);
|
||||
const history_item_t item = history_t::decode_item(mmap_start + offset, mmap_length - offset, mmap_type);
|
||||
if (! first)
|
||||
result.append(separator);
|
||||
result.append(item.str());
|
||||
@@ -456,7 +525,7 @@ history_item_t history_t::item_at_index(size_t idx) {
|
||||
if (idx < old_item_count) {
|
||||
/* idx=0 corresponds to last item in old_item_offsets */
|
||||
size_t offset = old_item_offsets.at(old_item_count - idx - 1);
|
||||
return history_t::decode_item(mmap_start + offset, mmap_length - offset);
|
||||
return history_t::decode_item(mmap_start + offset, mmap_length - offset, mmap_type);
|
||||
}
|
||||
|
||||
/* Index past the valid range, so return an empty history item */
|
||||
@@ -508,7 +577,8 @@ static bool extract_prefix(std::string &key, std::string &value, const std::stri
|
||||
return where != std::string::npos;
|
||||
}
|
||||
|
||||
history_item_t history_t::decode_item(const char *base, size_t len) {
|
||||
/* Decode an item via the fish 2.0 format */
|
||||
history_item_t history_t::decode_item_fish_2_0(const char *base, size_t len) {
|
||||
wcstring cmd;
|
||||
time_t when = 0;
|
||||
path_list_t paths;
|
||||
@@ -564,26 +634,160 @@ history_item_t history_t::decode_item(const char *base, size_t len) {
|
||||
/* We're going to consume this line */
|
||||
cursor += advance;
|
||||
|
||||
|
||||
/* Skip the leading dash-space and then store this path it */
|
||||
line.erase(0, 2);
|
||||
unescape_yaml(line);
|
||||
paths.push_front(str2wcstring(line));
|
||||
paths.push_back(str2wcstring(line));
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Reverse the paths, since we pushed them to the front each time */
|
||||
done:
|
||||
paths.reverse();
|
||||
return history_item_t(cmd, when, paths);
|
||||
}
|
||||
|
||||
history_item_t history_t::decode_item(const char *base, size_t len, history_file_type_t type) {
|
||||
switch (type) {
|
||||
case history_type_fish_1_x: return history_t::decode_item_fish_1_x(base, len);
|
||||
case history_type_fish_2_0: return history_t::decode_item_fish_2_0(base, len);
|
||||
default: return history_item_t(L"");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Remove backslashes from all newlines. This makes a string from the
|
||||
history file better formated for on screen display.
|
||||
*/
|
||||
static wcstring history_unescape_newlines_fish_1_x( const wcstring &in_str )
|
||||
{
|
||||
wcstring out;
|
||||
for (const wchar_t *in = in_str.c_str(); *in; in++)
|
||||
{
|
||||
if( *in == L'\\' )
|
||||
{
|
||||
if( *(in+1)!= L'\n')
|
||||
{
|
||||
out.push_back(*in);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
out.push_back(*in);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
/* Decode an item via the fish 1.x format. Adapted from fish 1.x's item_get(). */
|
||||
history_item_t history_t::decode_item_fish_1_x(const char *begin, size_t length) {
|
||||
|
||||
const char *end = begin + length;
|
||||
const char *pos=begin;
|
||||
|
||||
bool was_backslash = 0;
|
||||
wcstring out;
|
||||
bool first_char = true;
|
||||
bool timestamp_mode = false;
|
||||
time_t timestamp = 0;
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
wchar_t c;
|
||||
mbstate_t state;
|
||||
size_t res;
|
||||
|
||||
memset( &state, 0, sizeof(state) );
|
||||
|
||||
res = mbrtowc( &c, pos, end-pos, &state );
|
||||
|
||||
if( res == (size_t)-1 )
|
||||
{
|
||||
pos++;
|
||||
continue;
|
||||
}
|
||||
else if( res == (size_t)-2 )
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if( res == (size_t)0 )
|
||||
{
|
||||
pos++;
|
||||
continue;
|
||||
}
|
||||
pos += res;
|
||||
|
||||
if( c == L'\n' )
|
||||
{
|
||||
if( timestamp_mode )
|
||||
{
|
||||
const wchar_t *time_string = out.c_str();
|
||||
while( *time_string && !iswdigit(*time_string))
|
||||
time_string++;
|
||||
errno=0;
|
||||
|
||||
if( *time_string )
|
||||
{
|
||||
time_t tm;
|
||||
wchar_t *end;
|
||||
|
||||
errno = 0;
|
||||
tm = (time_t)wcstol( time_string, &end, 10 );
|
||||
|
||||
if( tm && !errno && !*end )
|
||||
{
|
||||
timestamp = tm;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
out.clear();
|
||||
timestamp_mode = false;
|
||||
continue;
|
||||
}
|
||||
if( !was_backslash )
|
||||
break;
|
||||
}
|
||||
|
||||
if( first_char )
|
||||
{
|
||||
if( c == L'#' )
|
||||
timestamp_mode = true;
|
||||
}
|
||||
|
||||
first_char = false;
|
||||
|
||||
out.push_back(c);
|
||||
|
||||
was_backslash = ( (c == L'\\') && !was_backslash);
|
||||
|
||||
}
|
||||
|
||||
out = history_unescape_newlines_fish_1_x(out);
|
||||
return history_item_t(out, timestamp);
|
||||
}
|
||||
|
||||
|
||||
/* Try to infer the history file type based on inspecting the data */
|
||||
static history_file_type_t infer_file_type(const char *data, size_t len) {
|
||||
history_file_type_t result = history_type_unknown;
|
||||
if (len > 0) {
|
||||
/* Old fish started with a # */
|
||||
if (data[0] == '#') {
|
||||
result = history_type_fish_1_x;
|
||||
} else {
|
||||
/* Assume new fish */
|
||||
result = history_type_fish_2_0;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void history_t::populate_from_mmap(void)
|
||||
{
|
||||
mmap_type = infer_file_type(mmap_start, mmap_length);
|
||||
size_t cursor = 0;
|
||||
for (;;) {
|
||||
size_t offset = offset_of_next_item(mmap_start, mmap_length, &cursor, birth_timestamp);
|
||||
size_t offset = offset_of_next_item(mmap_start, mmap_length, mmap_type, &cursor, birth_timestamp);
|
||||
// If we get back -1, we're done
|
||||
if (offset == (size_t)(-1))
|
||||
break;
|
||||
@@ -721,7 +925,7 @@ wcstring history_search_t::current_string() const {
|
||||
}
|
||||
|
||||
bool history_search_t::match_already_made(const wcstring &match) const {
|
||||
for (std::deque<prev_match_t>::const_iterator iter = prev_matches.begin(); iter != prev_matches.end(); ++iter) {
|
||||
for (std::vector<prev_match_t>::const_iterator iter = prev_matches.begin(); iter != prev_matches.end(); ++iter) {
|
||||
if (iter->second.str() == match)
|
||||
return true;
|
||||
}
|
||||
@@ -833,15 +1037,16 @@ void history_t::save_internal()
|
||||
const char *local_mmap_start = NULL;
|
||||
size_t local_mmap_size = 0;
|
||||
if (map_file(name, &local_mmap_start, &local_mmap_size)) {
|
||||
const history_file_type_t local_mmap_type = infer_file_type(local_mmap_start, local_mmap_size);
|
||||
size_t cursor = 0;
|
||||
for (;;) {
|
||||
size_t offset = offset_of_next_item(local_mmap_start, local_mmap_size, &cursor, 0);
|
||||
size_t offset = offset_of_next_item(local_mmap_start, local_mmap_size, local_mmap_type, &cursor, 0);
|
||||
/* If we get back -1, we're done */
|
||||
if (offset == (size_t)(-1))
|
||||
break;
|
||||
|
||||
/* Try decoding an old item */
|
||||
const history_item_t old_item = history_t::decode_item(local_mmap_start + offset, local_mmap_size - offset);
|
||||
const history_item_t old_item = history_t::decode_item(local_mmap_start + offset, local_mmap_size - offset, local_mmap_type);
|
||||
if (old_item.empty() || is_deleted(old_item))
|
||||
{
|
||||
// debug(0, L"Item is deleted : %s\n", old_item.str().c_str());
|
||||
@@ -915,12 +1120,14 @@ void history_t::save_internal()
|
||||
}
|
||||
}
|
||||
|
||||
void history_t::save(void) {
|
||||
void history_t::save(void)
|
||||
{
|
||||
scoped_lock locker(lock);
|
||||
this->save_internal();
|
||||
}
|
||||
|
||||
void history_t::clear(void) {
|
||||
void history_t::clear(void)
|
||||
{
|
||||
scoped_lock locker(lock);
|
||||
new_items.clear();
|
||||
deleted_items.clear();
|
||||
@@ -933,6 +1140,79 @@ void history_t::clear(void) {
|
||||
|
||||
}
|
||||
|
||||
bool history_t::is_empty(void)
|
||||
{
|
||||
bool result = false;
|
||||
scoped_lock locker(lock);
|
||||
if (new_items.empty())
|
||||
{
|
||||
load_old_if_needed();
|
||||
result = old_item_offsets.empty();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Indicate whether we ought to import the bash history file into fish */
|
||||
static bool should_import_bash_history_line(const std::string &line)
|
||||
{
|
||||
if (line.empty())
|
||||
return false;
|
||||
|
||||
/* Very naive tests! Skip export; probably should skip others. */
|
||||
const char * const ignore_prefixes[] = {
|
||||
"export ",
|
||||
"#"
|
||||
};
|
||||
|
||||
for (size_t i=0; i < sizeof ignore_prefixes / sizeof *ignore_prefixes; i++) {
|
||||
const char *prefix = ignore_prefixes[i];
|
||||
if (! line.compare(0, strlen(prefix), prefix)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Skip lines with backticks */
|
||||
if (line.find('`') != std::string::npos)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void history_t::populate_from_bash(FILE *stream)
|
||||
{
|
||||
/* Bash's format is very simple: just lines with #s for comments.
|
||||
Ignore a few commands that are bash-specific. This list ought to be expanded.
|
||||
*/
|
||||
std::string line;
|
||||
for (;;) {
|
||||
line.clear();
|
||||
bool success = false, has_newline = false;
|
||||
|
||||
/* Loop until we've read a line */
|
||||
do {
|
||||
char buff[128];
|
||||
success = !! fgets(buff, sizeof buff, stream);
|
||||
if (success) {
|
||||
/* Skip the newline */
|
||||
char *newline = strchr(buff, '\n');
|
||||
if (newline) *newline = '\0';
|
||||
has_newline = (newline != NULL);
|
||||
|
||||
/* Append what we've got */
|
||||
line.append(buff);
|
||||
}
|
||||
} while (success && ! has_newline);
|
||||
|
||||
/* Maybe add this line */
|
||||
if (should_import_bash_history_line(line)) {
|
||||
this->add(str2wcstring(line));
|
||||
}
|
||||
|
||||
if (line.empty())
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void history_init()
|
||||
{
|
||||
}
|
||||
@@ -961,7 +1241,7 @@ int file_detection_context_t::perform_file_detection(bool test_all) {
|
||||
for (path_list_t::const_iterator iter = potential_paths.begin(); iter != potential_paths.end(); ++iter) {
|
||||
if (path_is_valid(*iter, working_directory)) {
|
||||
/* Push the original (possibly relative) path */
|
||||
valid_paths.push_front(*iter);
|
||||
valid_paths.push_back(*iter);
|
||||
} else {
|
||||
/* Not a valid path */
|
||||
result = 0;
|
||||
@@ -969,7 +1249,6 @@ int file_detection_context_t::perform_file_detection(bool test_all) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
valid_paths.reverse();
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1023,7 +1302,7 @@ void history_t::add_with_file_detection(const wcstring &str)
|
||||
if (token_cstr) {
|
||||
wcstring potential_path = token_cstr;
|
||||
if (unescape_string(potential_path, false) && string_could_be_path(potential_path)) {
|
||||
potential_paths.push_front(potential_path);
|
||||
potential_paths.push_back(potential_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1035,7 +1314,6 @@ void history_t::add_with_file_detection(const wcstring &str)
|
||||
file_detection_context_t *context = new file_detection_context_t(this, str);
|
||||
|
||||
/* Store the potential paths. Reverse them to put them in the same order as in the command. */
|
||||
potential_paths.reverse();
|
||||
context->potential_paths.swap(potential_paths);
|
||||
iothread_perform(threaded_perform_file_detection, perform_file_detection_done, context);
|
||||
}
|
||||
@@ -1043,10 +1321,5 @@ void history_t::add_with_file_detection(const wcstring &str)
|
||||
|
||||
bool history_t::is_deleted(const history_item_t &item) const
|
||||
{
|
||||
for (std::vector<history_item_t>::const_iterator iter = deleted_items.begin(); iter != deleted_items.end(); ++iter)
|
||||
{
|
||||
if (iter->match_contents(item)) { return true; }
|
||||
}
|
||||
|
||||
return false;
|
||||
return deleted_items.count(item.str()) > 0;
|
||||
}
|
||||
|
||||
46
history.h
46
history.h
@@ -9,13 +9,11 @@
|
||||
#include "common.h"
|
||||
#include "pthread.h"
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <utility>
|
||||
#include <list>
|
||||
#include <tr1/memory>
|
||||
#include <set>
|
||||
|
||||
typedef std::list<wcstring> path_list_t;
|
||||
typedef std::vector<wcstring> path_list_t;
|
||||
|
||||
enum history_search_type_t {
|
||||
/** The history searches for strings containing the given string */
|
||||
@@ -57,20 +55,18 @@ class history_item_t {
|
||||
|
||||
const path_list_t &get_required_paths() const { return required_paths; }
|
||||
|
||||
bool write_to_file(FILE *f) const;
|
||||
|
||||
bool operator==(const history_item_t &other) const {
|
||||
return contents == other.contents &&
|
||||
creation_timestamp == other.creation_timestamp &&
|
||||
required_paths == other.required_paths;
|
||||
}
|
||||
};
|
||||
|
||||
bool match_contents(const history_item_t &other) const {
|
||||
return contents == other.contents;
|
||||
}
|
||||
|
||||
/* Functions for testing only */
|
||||
|
||||
/* The type of file that we mmap'd */
|
||||
enum history_file_type_t {
|
||||
history_type_unknown,
|
||||
history_type_fish_2_0,
|
||||
history_type_fish_1_x
|
||||
};
|
||||
|
||||
class history_t {
|
||||
@@ -101,8 +97,8 @@ class history_t {
|
||||
/** New items. */
|
||||
std::vector<history_item_t> new_items;
|
||||
|
||||
/** Deleted items. */
|
||||
std::vector<history_item_t> deleted_items;
|
||||
/** Deleted item contents. */
|
||||
std::set<wcstring> deleted_items;
|
||||
|
||||
/** How many items we've added without saving */
|
||||
size_t unsaved_item_count;
|
||||
@@ -110,21 +106,22 @@ class history_t {
|
||||
/** The mmaped region for the history file */
|
||||
const char *mmap_start;
|
||||
|
||||
/** The size of the mmaped region */
|
||||
/** The size of the mmap'd region */
|
||||
size_t mmap_length;
|
||||
|
||||
/** The type of file we mmap'd */
|
||||
history_file_type_t mmap_type;
|
||||
|
||||
/** Timestamp of when this history was created */
|
||||
const time_t birth_timestamp;
|
||||
|
||||
/** Timestamp of last save */
|
||||
time_t save_timestamp;
|
||||
|
||||
static history_item_t decode_item(const char *ptr, size_t len);
|
||||
|
||||
void populate_from_mmap(void);
|
||||
|
||||
/** List of old items, as offsets into out mmap data */
|
||||
std::deque<size_t> old_item_offsets;
|
||||
std::vector<size_t> old_item_offsets;
|
||||
|
||||
/** Whether we've loaded old items */
|
||||
bool loaded_old;
|
||||
@@ -137,11 +134,19 @@ class history_t {
|
||||
|
||||
/** Saves history */
|
||||
void save_internal();
|
||||
|
||||
/* Versioned decoding */
|
||||
static history_item_t decode_item_fish_2_0(const char *base, size_t len);
|
||||
static history_item_t decode_item_fish_1_x(const char *base, size_t len);
|
||||
static history_item_t decode_item(const char *base, size_t len, history_file_type_t type);
|
||||
|
||||
public:
|
||||
/** Returns history with the given name, creating it if necessary */
|
||||
static history_t & history_with_name(const wcstring &name);
|
||||
|
||||
/** Determines whether the history is empty. Unfortunately this cannot be const, since it may require populating the history. */
|
||||
bool is_empty(void);
|
||||
|
||||
/** Add a new history item to the end */
|
||||
void add(const wcstring &str, const path_list_t &valid_paths = path_list_t());
|
||||
|
||||
@@ -157,12 +162,15 @@ class history_t {
|
||||
/** Irreversibly clears history */
|
||||
void clear();
|
||||
|
||||
/** Populates from a bash history file */
|
||||
void populate_from_bash(FILE *f);
|
||||
|
||||
/* Gets all the history into a string with ARRAY_SEP_STR. This is intended for the $history environment variable. This may be long! */
|
||||
void get_string_representation(wcstring &str, const wcstring &separator);
|
||||
|
||||
/** Return the specified history at the specified index. 0 is the index of the current commandline. (So the most recent item is at index 1.) */
|
||||
history_item_t item_at_index(size_t idx);
|
||||
|
||||
|
||||
bool is_deleted(const history_item_t &item) const;
|
||||
};
|
||||
|
||||
@@ -176,7 +184,7 @@ class history_search_t {
|
||||
|
||||
/** Our list of previous matches as index, value. The end is the current match. */
|
||||
typedef std::pair<size_t, history_item_t> prev_match_t;
|
||||
std::deque<prev_match_t> prev_matches;
|
||||
std::vector<prev_match_t> prev_matches;
|
||||
|
||||
/** Returns yes if a given term is in prev_matches. */
|
||||
bool match_already_made(const wcstring &match) const;
|
||||
|
||||
375
input.cpp
375
input.cpp
@@ -95,7 +95,7 @@ struct terminfo_mapping_t
|
||||
/**
|
||||
Names of all the input functions supported
|
||||
*/
|
||||
static const wchar_t *name_arr[] =
|
||||
static const wchar_t * const name_arr[] =
|
||||
{
|
||||
L"beginning-of-line",
|
||||
L"end-of-line",
|
||||
@@ -129,7 +129,9 @@ static const wchar_t *name_arr[] =
|
||||
L"end-of-buffer",
|
||||
L"repaint",
|
||||
L"up-line",
|
||||
L"down-line"
|
||||
L"down-line",
|
||||
L"suppress-autosuggestion",
|
||||
L"accept-autosuggestion"
|
||||
}
|
||||
;
|
||||
|
||||
@@ -160,7 +162,7 @@ static const wchar_t *desc_arr[] =
|
||||
L"Move entire line to killring",
|
||||
L"Move next word to killring",
|
||||
L"Move previous word to killring",
|
||||
L"Write out keybindings",
|
||||
L"Write out key bindings",
|
||||
L"Clear entire screen",
|
||||
L"Quit the running program",
|
||||
L"Search backward through list of previous commands for matching token",
|
||||
@@ -210,7 +212,9 @@ static const wchar_t code_arr[] =
|
||||
R_END_OF_BUFFER,
|
||||
R_REPAINT,
|
||||
R_UP_LINE,
|
||||
R_DOWN_LINE
|
||||
R_DOWN_LINE,
|
||||
R_SUPPRESS_AUTOSUGGESTION,
|
||||
R_ACCEPT_AUTOSUGGESTION
|
||||
}
|
||||
;
|
||||
|
||||
@@ -220,14 +224,7 @@ static std::vector<input_mapping_t> mapping_list;
|
||||
/* Terminfo map list */
|
||||
static std::vector<terminfo_mapping_t> terminfo_mappings;
|
||||
|
||||
/** Add a new terminfo mapping */
|
||||
static inline void terminfo_add(const wchar_t *name, const char *seq)
|
||||
{
|
||||
terminfo_mapping_t mapping = {name, seq};
|
||||
terminfo_mappings.push_back(mapping);
|
||||
}
|
||||
#define TERMINFO_ADD(key) do { terminfo_add((L ## #key)+4, key); } while (0)
|
||||
|
||||
#define TERMINFO_ADD(key) { (L ## #key) + 4, key }
|
||||
|
||||
|
||||
/**
|
||||
@@ -255,7 +252,6 @@ void input_mapping_add( const wchar_t *sequence,
|
||||
const wchar_t *command )
|
||||
{
|
||||
size_t i;
|
||||
|
||||
CHECK( sequence, );
|
||||
CHECK( command, );
|
||||
|
||||
@@ -354,7 +350,7 @@ int input_init()
|
||||
update_fish_term256();
|
||||
|
||||
/* If we have no keybindings, add a few simple defaults */
|
||||
if( mapping_list.size() )
|
||||
if( mapping_list.empty() )
|
||||
{
|
||||
input_mapping_add( L"", L"self-insert" );
|
||||
input_mapping_add( L"\n", L"execute" );
|
||||
@@ -415,7 +411,7 @@ static wint_t input_exec_binding( const input_mapping_t &m, const wcstring &seq
|
||||
*/
|
||||
int last_status = proc_get_last_status();
|
||||
|
||||
parser_t::principal_parser().eval( m.command.c_str(), 0, TOP );
|
||||
parser_t::principal_parser().eval( m.command.c_str(), io_chain_t(), TOP );
|
||||
|
||||
proc_set_last_status( last_status );
|
||||
|
||||
@@ -443,7 +439,7 @@ static wint_t input_try_mapping( const input_mapping_t &m)
|
||||
{
|
||||
wint_t c=0;
|
||||
int j;
|
||||
|
||||
|
||||
/*
|
||||
Check if the actual function code of this mapping is on the stack
|
||||
*/
|
||||
@@ -493,18 +489,18 @@ wint_t input_readch()
|
||||
{
|
||||
|
||||
size_t i;
|
||||
|
||||
|
||||
CHECK_BLOCK( R_NULL );
|
||||
|
||||
/*
|
||||
Clear the interrupted flag
|
||||
*/
|
||||
Clear the interrupted flag
|
||||
*/
|
||||
reader_interrupted();
|
||||
|
||||
/*
|
||||
Search for sequence in mapping tables
|
||||
*/
|
||||
|
||||
Search for sequence in mapping tables
|
||||
*/
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
const input_mapping_t *generic = 0;
|
||||
@@ -523,27 +519,34 @@ wint_t input_readch()
|
||||
}
|
||||
|
||||
/*
|
||||
No matching exact mapping, try to find generic mapping.
|
||||
*/
|
||||
|
||||
No matching exact mapping, try to find generic mapping.
|
||||
*/
|
||||
|
||||
if( generic )
|
||||
{
|
||||
wchar_t arr[2]=
|
||||
{
|
||||
0,
|
||||
0
|
||||
}
|
||||
{
|
||||
0,
|
||||
0
|
||||
}
|
||||
;
|
||||
arr[0] = input_common_readch(0);
|
||||
|
||||
return input_exec_binding( *generic, arr );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
No action to take on specified character, ignore it
|
||||
and move to next one.
|
||||
*/
|
||||
input_common_readch( 0 ); }
|
||||
No action to take on specified character, ignore it
|
||||
and move to next one.
|
||||
*/
|
||||
wchar_t c = input_common_readch( 0 );
|
||||
|
||||
/* If it's closed, then just return */
|
||||
if (c == R_EOF)
|
||||
{
|
||||
return WEOF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void input_mapping_get_names( wcstring_list_t &lst )
|
||||
@@ -604,52 +607,53 @@ bool input_mapping_get( const wcstring &sequence, wcstring &cmd )
|
||||
*/
|
||||
static void input_terminfo_init()
|
||||
{
|
||||
TERMINFO_ADD(key_a1);
|
||||
TERMINFO_ADD(key_a3);
|
||||
TERMINFO_ADD(key_b2);
|
||||
TERMINFO_ADD(key_backspace);
|
||||
TERMINFO_ADD(key_beg);
|
||||
TERMINFO_ADD(key_btab);
|
||||
TERMINFO_ADD(key_c1);
|
||||
TERMINFO_ADD(key_c3);
|
||||
TERMINFO_ADD(key_cancel);
|
||||
TERMINFO_ADD(key_catab);
|
||||
TERMINFO_ADD(key_clear);
|
||||
TERMINFO_ADD(key_close);
|
||||
TERMINFO_ADD(key_command);
|
||||
TERMINFO_ADD(key_copy);
|
||||
TERMINFO_ADD(key_create);
|
||||
TERMINFO_ADD(key_ctab);
|
||||
TERMINFO_ADD(key_dc);
|
||||
TERMINFO_ADD(key_dl);
|
||||
TERMINFO_ADD(key_down);
|
||||
TERMINFO_ADD(key_eic);
|
||||
TERMINFO_ADD(key_end);
|
||||
TERMINFO_ADD(key_enter);
|
||||
TERMINFO_ADD(key_eol);
|
||||
TERMINFO_ADD(key_eos);
|
||||
TERMINFO_ADD(key_exit);
|
||||
TERMINFO_ADD(key_f0);
|
||||
TERMINFO_ADD(key_f1);
|
||||
TERMINFO_ADD(key_f2);
|
||||
TERMINFO_ADD(key_f3);
|
||||
TERMINFO_ADD(key_f4);
|
||||
TERMINFO_ADD(key_f5);
|
||||
TERMINFO_ADD(key_f6);
|
||||
TERMINFO_ADD(key_f7);
|
||||
TERMINFO_ADD(key_f8);
|
||||
TERMINFO_ADD(key_f9);
|
||||
TERMINFO_ADD(key_f10);
|
||||
TERMINFO_ADD(key_f11);
|
||||
TERMINFO_ADD(key_f12);
|
||||
TERMINFO_ADD(key_f13);
|
||||
TERMINFO_ADD(key_f14);
|
||||
TERMINFO_ADD(key_f15);
|
||||
TERMINFO_ADD(key_f16);
|
||||
TERMINFO_ADD(key_f17);
|
||||
TERMINFO_ADD(key_f18);
|
||||
TERMINFO_ADD(key_f19);
|
||||
TERMINFO_ADD(key_f20);
|
||||
const terminfo_mapping_t tinfos[] = {
|
||||
TERMINFO_ADD(key_a1),
|
||||
TERMINFO_ADD(key_a3),
|
||||
TERMINFO_ADD(key_b2),
|
||||
TERMINFO_ADD(key_backspace),
|
||||
TERMINFO_ADD(key_beg),
|
||||
TERMINFO_ADD(key_btab),
|
||||
TERMINFO_ADD(key_c1),
|
||||
TERMINFO_ADD(key_c3),
|
||||
TERMINFO_ADD(key_cancel),
|
||||
TERMINFO_ADD(key_catab),
|
||||
TERMINFO_ADD(key_clear),
|
||||
TERMINFO_ADD(key_close),
|
||||
TERMINFO_ADD(key_command),
|
||||
TERMINFO_ADD(key_copy),
|
||||
TERMINFO_ADD(key_create),
|
||||
TERMINFO_ADD(key_ctab),
|
||||
TERMINFO_ADD(key_dc),
|
||||
TERMINFO_ADD(key_dl),
|
||||
TERMINFO_ADD(key_down),
|
||||
TERMINFO_ADD(key_eic),
|
||||
TERMINFO_ADD(key_end),
|
||||
TERMINFO_ADD(key_enter),
|
||||
TERMINFO_ADD(key_eol),
|
||||
TERMINFO_ADD(key_eos),
|
||||
TERMINFO_ADD(key_exit),
|
||||
TERMINFO_ADD(key_f0),
|
||||
TERMINFO_ADD(key_f1),
|
||||
TERMINFO_ADD(key_f2),
|
||||
TERMINFO_ADD(key_f3),
|
||||
TERMINFO_ADD(key_f4),
|
||||
TERMINFO_ADD(key_f5),
|
||||
TERMINFO_ADD(key_f6),
|
||||
TERMINFO_ADD(key_f7),
|
||||
TERMINFO_ADD(key_f8),
|
||||
TERMINFO_ADD(key_f9),
|
||||
TERMINFO_ADD(key_f10),
|
||||
TERMINFO_ADD(key_f11),
|
||||
TERMINFO_ADD(key_f12),
|
||||
TERMINFO_ADD(key_f13),
|
||||
TERMINFO_ADD(key_f14),
|
||||
TERMINFO_ADD(key_f15),
|
||||
TERMINFO_ADD(key_f16),
|
||||
TERMINFO_ADD(key_f17),
|
||||
TERMINFO_ADD(key_f18),
|
||||
TERMINFO_ADD(key_f19),
|
||||
TERMINFO_ADD(key_f20),
|
||||
/*
|
||||
I know of no keyboard with more than 20 function keys, so
|
||||
adding the rest here makes very little sense, since it will
|
||||
@@ -657,109 +661,113 @@ static void input_terminfo_init()
|
||||
but with no benefit.
|
||||
*/
|
||||
/*
|
||||
TERMINFO_ADD(key_f21);
|
||||
TERMINFO_ADD(key_f22);
|
||||
TERMINFO_ADD(key_f23);
|
||||
TERMINFO_ADD(key_f24);
|
||||
TERMINFO_ADD(key_f25);
|
||||
TERMINFO_ADD(key_f26);
|
||||
TERMINFO_ADD(key_f27);
|
||||
TERMINFO_ADD(key_f28);
|
||||
TERMINFO_ADD(key_f29);
|
||||
TERMINFO_ADD(key_f30);
|
||||
TERMINFO_ADD(key_f31);
|
||||
TERMINFO_ADD(key_f32);
|
||||
TERMINFO_ADD(key_f33);
|
||||
TERMINFO_ADD(key_f34);
|
||||
TERMINFO_ADD(key_f35);
|
||||
TERMINFO_ADD(key_f36);
|
||||
TERMINFO_ADD(key_f37);
|
||||
TERMINFO_ADD(key_f38);
|
||||
TERMINFO_ADD(key_f39);
|
||||
TERMINFO_ADD(key_f40);
|
||||
TERMINFO_ADD(key_f41);
|
||||
TERMINFO_ADD(key_f42);
|
||||
TERMINFO_ADD(key_f43);
|
||||
TERMINFO_ADD(key_f44);
|
||||
TERMINFO_ADD(key_f45);
|
||||
TERMINFO_ADD(key_f46);
|
||||
TERMINFO_ADD(key_f47);
|
||||
TERMINFO_ADD(key_f48);
|
||||
TERMINFO_ADD(key_f49);
|
||||
TERMINFO_ADD(key_f50);
|
||||
TERMINFO_ADD(key_f51);
|
||||
TERMINFO_ADD(key_f52);
|
||||
TERMINFO_ADD(key_f53);
|
||||
TERMINFO_ADD(key_f54);
|
||||
TERMINFO_ADD(key_f55);
|
||||
TERMINFO_ADD(key_f56);
|
||||
TERMINFO_ADD(key_f57);
|
||||
TERMINFO_ADD(key_f58);
|
||||
TERMINFO_ADD(key_f59);
|
||||
TERMINFO_ADD(key_f60);
|
||||
TERMINFO_ADD(key_f61);
|
||||
TERMINFO_ADD(key_f62);
|
||||
TERMINFO_ADD(key_f63);*/
|
||||
TERMINFO_ADD(key_find);
|
||||
TERMINFO_ADD(key_help);
|
||||
TERMINFO_ADD(key_home);
|
||||
TERMINFO_ADD(key_ic);
|
||||
TERMINFO_ADD(key_il);
|
||||
TERMINFO_ADD(key_left);
|
||||
TERMINFO_ADD(key_ll);
|
||||
TERMINFO_ADD(key_mark);
|
||||
TERMINFO_ADD(key_message);
|
||||
TERMINFO_ADD(key_move);
|
||||
TERMINFO_ADD(key_next);
|
||||
TERMINFO_ADD(key_npage);
|
||||
TERMINFO_ADD(key_open);
|
||||
TERMINFO_ADD(key_options);
|
||||
TERMINFO_ADD(key_ppage);
|
||||
TERMINFO_ADD(key_previous);
|
||||
TERMINFO_ADD(key_print);
|
||||
TERMINFO_ADD(key_redo);
|
||||
TERMINFO_ADD(key_reference);
|
||||
TERMINFO_ADD(key_refresh);
|
||||
TERMINFO_ADD(key_replace);
|
||||
TERMINFO_ADD(key_restart);
|
||||
TERMINFO_ADD(key_resume);
|
||||
TERMINFO_ADD(key_right);
|
||||
TERMINFO_ADD(key_save);
|
||||
TERMINFO_ADD(key_sbeg);
|
||||
TERMINFO_ADD(key_scancel);
|
||||
TERMINFO_ADD(key_scommand);
|
||||
TERMINFO_ADD(key_scopy);
|
||||
TERMINFO_ADD(key_screate);
|
||||
TERMINFO_ADD(key_sdc);
|
||||
TERMINFO_ADD(key_sdl);
|
||||
TERMINFO_ADD(key_select);
|
||||
TERMINFO_ADD(key_send);
|
||||
TERMINFO_ADD(key_seol);
|
||||
TERMINFO_ADD(key_sexit);
|
||||
TERMINFO_ADD(key_sf);
|
||||
TERMINFO_ADD(key_sfind);
|
||||
TERMINFO_ADD(key_shelp);
|
||||
TERMINFO_ADD(key_shome);
|
||||
TERMINFO_ADD(key_sic);
|
||||
TERMINFO_ADD(key_sleft);
|
||||
TERMINFO_ADD(key_smessage);
|
||||
TERMINFO_ADD(key_smove);
|
||||
TERMINFO_ADD(key_snext);
|
||||
TERMINFO_ADD(key_soptions);
|
||||
TERMINFO_ADD(key_sprevious);
|
||||
TERMINFO_ADD(key_sprint);
|
||||
TERMINFO_ADD(key_sr);
|
||||
TERMINFO_ADD(key_sredo);
|
||||
TERMINFO_ADD(key_sreplace);
|
||||
TERMINFO_ADD(key_sright);
|
||||
TERMINFO_ADD(key_srsume);
|
||||
TERMINFO_ADD(key_ssave);
|
||||
TERMINFO_ADD(key_ssuspend);
|
||||
TERMINFO_ADD(key_stab);
|
||||
TERMINFO_ADD(key_sundo);
|
||||
TERMINFO_ADD(key_suspend);
|
||||
TERMINFO_ADD(key_undo);
|
||||
TERMINFO_ADD(key_up);
|
||||
TERMINFO_ADD(key_f21),
|
||||
TERMINFO_ADD(key_f22),
|
||||
TERMINFO_ADD(key_f23),
|
||||
TERMINFO_ADD(key_f24),
|
||||
TERMINFO_ADD(key_f25),
|
||||
TERMINFO_ADD(key_f26),
|
||||
TERMINFO_ADD(key_f27),
|
||||
TERMINFO_ADD(key_f28),
|
||||
TERMINFO_ADD(key_f29),
|
||||
TERMINFO_ADD(key_f30),
|
||||
TERMINFO_ADD(key_f31),
|
||||
TERMINFO_ADD(key_f32),
|
||||
TERMINFO_ADD(key_f33),
|
||||
TERMINFO_ADD(key_f34),
|
||||
TERMINFO_ADD(key_f35),
|
||||
TERMINFO_ADD(key_f36),
|
||||
TERMINFO_ADD(key_f37),
|
||||
TERMINFO_ADD(key_f38),
|
||||
TERMINFO_ADD(key_f39),
|
||||
TERMINFO_ADD(key_f40),
|
||||
TERMINFO_ADD(key_f41),
|
||||
TERMINFO_ADD(key_f42),
|
||||
TERMINFO_ADD(key_f43),
|
||||
TERMINFO_ADD(key_f44),
|
||||
TERMINFO_ADD(key_f45),
|
||||
TERMINFO_ADD(key_f46),
|
||||
TERMINFO_ADD(key_f47),
|
||||
TERMINFO_ADD(key_f48),
|
||||
TERMINFO_ADD(key_f49),
|
||||
TERMINFO_ADD(key_f50),
|
||||
TERMINFO_ADD(key_f51),
|
||||
TERMINFO_ADD(key_f52),
|
||||
TERMINFO_ADD(key_f53),
|
||||
TERMINFO_ADD(key_f54),
|
||||
TERMINFO_ADD(key_f55),
|
||||
TERMINFO_ADD(key_f56),
|
||||
TERMINFO_ADD(key_f57),
|
||||
TERMINFO_ADD(key_f58),
|
||||
TERMINFO_ADD(key_f59),
|
||||
TERMINFO_ADD(key_f60),
|
||||
TERMINFO_ADD(key_f61),
|
||||
TERMINFO_ADD(key_f62),
|
||||
TERMINFO_ADD(key_f63),*/
|
||||
TERMINFO_ADD(key_find),
|
||||
TERMINFO_ADD(key_help),
|
||||
TERMINFO_ADD(key_home),
|
||||
TERMINFO_ADD(key_ic),
|
||||
TERMINFO_ADD(key_il),
|
||||
TERMINFO_ADD(key_left),
|
||||
TERMINFO_ADD(key_ll),
|
||||
TERMINFO_ADD(key_mark),
|
||||
TERMINFO_ADD(key_message),
|
||||
TERMINFO_ADD(key_move),
|
||||
TERMINFO_ADD(key_next),
|
||||
TERMINFO_ADD(key_npage),
|
||||
TERMINFO_ADD(key_open),
|
||||
TERMINFO_ADD(key_options),
|
||||
TERMINFO_ADD(key_ppage),
|
||||
TERMINFO_ADD(key_previous),
|
||||
TERMINFO_ADD(key_print),
|
||||
TERMINFO_ADD(key_redo),
|
||||
TERMINFO_ADD(key_reference),
|
||||
TERMINFO_ADD(key_refresh),
|
||||
TERMINFO_ADD(key_replace),
|
||||
TERMINFO_ADD(key_restart),
|
||||
TERMINFO_ADD(key_resume),
|
||||
TERMINFO_ADD(key_right),
|
||||
TERMINFO_ADD(key_save),
|
||||
TERMINFO_ADD(key_sbeg),
|
||||
TERMINFO_ADD(key_scancel),
|
||||
TERMINFO_ADD(key_scommand),
|
||||
TERMINFO_ADD(key_scopy),
|
||||
TERMINFO_ADD(key_screate),
|
||||
TERMINFO_ADD(key_sdc),
|
||||
TERMINFO_ADD(key_sdl),
|
||||
TERMINFO_ADD(key_select),
|
||||
TERMINFO_ADD(key_send),
|
||||
TERMINFO_ADD(key_seol),
|
||||
TERMINFO_ADD(key_sexit),
|
||||
TERMINFO_ADD(key_sf),
|
||||
TERMINFO_ADD(key_sfind),
|
||||
TERMINFO_ADD(key_shelp),
|
||||
TERMINFO_ADD(key_shome),
|
||||
TERMINFO_ADD(key_sic),
|
||||
TERMINFO_ADD(key_sleft),
|
||||
TERMINFO_ADD(key_smessage),
|
||||
TERMINFO_ADD(key_smove),
|
||||
TERMINFO_ADD(key_snext),
|
||||
TERMINFO_ADD(key_soptions),
|
||||
TERMINFO_ADD(key_sprevious),
|
||||
TERMINFO_ADD(key_sprint),
|
||||
TERMINFO_ADD(key_sr),
|
||||
TERMINFO_ADD(key_sredo),
|
||||
TERMINFO_ADD(key_sreplace),
|
||||
TERMINFO_ADD(key_sright),
|
||||
TERMINFO_ADD(key_srsume),
|
||||
TERMINFO_ADD(key_ssave),
|
||||
TERMINFO_ADD(key_ssuspend),
|
||||
TERMINFO_ADD(key_stab),
|
||||
TERMINFO_ADD(key_sundo),
|
||||
TERMINFO_ADD(key_suspend),
|
||||
TERMINFO_ADD(key_undo),
|
||||
TERMINFO_ADD(key_up)
|
||||
};
|
||||
const size_t count = sizeof tinfos / sizeof *tinfos;
|
||||
terminfo_mappings.reserve(terminfo_mappings.size() + count);
|
||||
terminfo_mappings.insert(terminfo_mappings.end(), tinfos, tinfos + count);
|
||||
}
|
||||
|
||||
const wchar_t *input_terminfo_get_sequence( const wchar_t *name )
|
||||
@@ -857,4 +865,3 @@ wchar_t input_function_get_code( const wcstring &name )
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
2
input.h
2
input.h
@@ -49,6 +49,8 @@ enum
|
||||
R_REPAINT,
|
||||
R_UP_LINE,
|
||||
R_DOWN_LINE,
|
||||
R_SUPPRESS_AUTOSUGGESTION,
|
||||
R_ACCEPT_AUTOSUGGESTION
|
||||
}
|
||||
;
|
||||
|
||||
|
||||
@@ -217,7 +217,7 @@ wchar_t input_common_readch( int timed )
|
||||
wint_t b = readb();
|
||||
char bb;
|
||||
|
||||
int sz;
|
||||
size_t sz;
|
||||
|
||||
if( (b >= R_NULL) && (b < R_NULL + 1000) )
|
||||
return b;
|
||||
@@ -228,11 +228,11 @@ wchar_t input_common_readch( int timed )
|
||||
|
||||
switch( sz )
|
||||
{
|
||||
case -1:
|
||||
case (size_t)(-1):
|
||||
memset (&state, '\0', sizeof (state));
|
||||
debug( 2, L"Illegal input" );
|
||||
return R_NULL;
|
||||
case -2:
|
||||
case (size_t)(-2):
|
||||
break;
|
||||
case 0:
|
||||
return 0;
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#include <wchar.h>
|
||||
#include <unistd.h>
|
||||
#include <set>
|
||||
#include <deque>
|
||||
#include <algorithm>
|
||||
|
||||
#include "fallback.h"
|
||||
@@ -29,14 +28,14 @@ class string_table_compare_t {
|
||||
}
|
||||
};
|
||||
|
||||
/* A sorted deque ends up being a little more memory efficient than a std::set for the intern'd string table */
|
||||
/* A sorted vector ends up being a little more memory efficient than a std::set for the intern'd string table */
|
||||
#define USE_SET 0
|
||||
#if USE_SET
|
||||
/** The table of intern'd strings */
|
||||
typedef std::set<const wchar_t *, string_table_compare_t> string_table_t;
|
||||
#else
|
||||
/** The table of intern'd strings */
|
||||
typedef std::deque<const wchar_t *> string_table_t;
|
||||
typedef std::vector<const wchar_t *> string_table_t;
|
||||
#endif
|
||||
|
||||
static string_table_t string_table;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
|
||||
import string, sys, os.path
|
||||
import string, sys, os.path, getopt
|
||||
|
||||
escapes = {}
|
||||
escapes['\a'] = r'\a'
|
||||
@@ -64,7 +64,34 @@ class cfunc:
|
||||
|
||||
TYPES = ['function', 'completion']
|
||||
type_to_funcs = dict((t, []) for t in TYPES)
|
||||
for file in sys.argv[1:]:
|
||||
|
||||
def usage(script_name):
|
||||
print("Usage: {0} [--output output_directory] files...".format(script_name))
|
||||
print("""Command options are:
|
||||
--output directory\t\tThe directory to output the files
|
||||
-h, --help\t\t\tShow this help message
|
||||
""")
|
||||
|
||||
script_name = sys.argv[0]
|
||||
try:
|
||||
opts, file_paths = getopt.gnu_getopt(sys.argv[1:], 'h', ['output=', 'help'])
|
||||
except getopt.GetoptError as err:
|
||||
print(err.msg) # will print something like "option -a not recognized"
|
||||
usage(script_name)
|
||||
sys.exit(2)
|
||||
|
||||
output_directory = './'
|
||||
for opt, value in opts:
|
||||
if opt in ('--output',):
|
||||
output_directory = value
|
||||
elif opt in ('-h', '--help'):
|
||||
usage(script_name)
|
||||
sys.exit(0)
|
||||
else:
|
||||
assert False, "unhandled option"
|
||||
|
||||
|
||||
for file in file_paths:
|
||||
fd = open(file, 'r')
|
||||
newlines = []
|
||||
for line in fd:
|
||||
@@ -89,7 +116,7 @@ for funcs in type_to_funcs.values():
|
||||
funcs.sort(key=cfunc.cfunc_name)
|
||||
|
||||
# Output our header
|
||||
fd = open('builtin_scripts.h', 'w')
|
||||
fd = open(os.path.join(output_directory, 'builtin_scripts.h'), 'w')
|
||||
fd.write('/* This file is generated by internalize_scripts.py */\n\n')
|
||||
fd.write("""struct builtin_script_t {
|
||||
const wchar_t *name;
|
||||
@@ -106,7 +133,7 @@ for type in TYPES:
|
||||
fd.close()
|
||||
|
||||
# Output the function definitions
|
||||
fd = open('builtin_scripts.cpp', 'w')
|
||||
fd = open(os.path.join(output_directory, 'builtin_scripts.cpp'), 'w')
|
||||
fd.write('/* This file is generated by internalize_scripts.py */\n\n')
|
||||
fd.write('#include "builtin_scripts.h"\n\n')
|
||||
for type in TYPES:
|
||||
|
||||
238
io.cpp
238
io.cpp
@@ -12,6 +12,8 @@ Utilities for io redirection.
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <set>
|
||||
#include <algorithm>
|
||||
|
||||
#ifdef HAVE_SYS_TERMIOS_H
|
||||
#include <sys/termios.h>
|
||||
@@ -64,7 +66,7 @@ void io_buffer_read( io_data_t *d )
|
||||
while(1)
|
||||
{
|
||||
char b[4096];
|
||||
int l;
|
||||
long l;
|
||||
l=read_blocked( d->param1.pipe_fd[0], b, 4096 );
|
||||
if( l==0 )
|
||||
{
|
||||
@@ -98,20 +100,20 @@ void io_buffer_read( io_data_t *d )
|
||||
}
|
||||
|
||||
|
||||
io_data_t *io_buffer_create( int is_input )
|
||||
io_data_t *io_buffer_create( bool is_input )
|
||||
{
|
||||
std::auto_ptr<io_data_t> buffer_redirect(new io_data_t);
|
||||
bool success = true;
|
||||
io_data_t *buffer_redirect = new io_data_t;
|
||||
buffer_redirect->out_buffer_create();
|
||||
buffer_redirect->io_mode=IO_BUFFER;
|
||||
buffer_redirect->next=0;
|
||||
buffer_redirect->is_input = is_input;
|
||||
buffer_redirect->io_mode = IO_BUFFER;
|
||||
buffer_redirect->is_input = is_input ? true : false;
|
||||
buffer_redirect->fd=is_input?0:1;
|
||||
|
||||
if( exec_pipe( buffer_redirect->param1.pipe_fd ) == -1 )
|
||||
{
|
||||
debug( 1, PIPE_ERROR );
|
||||
wperror (L"pipe");
|
||||
return NULL;
|
||||
success = false;
|
||||
}
|
||||
else if( fcntl( buffer_redirect->param1.pipe_fd[0],
|
||||
F_SETFL,
|
||||
@@ -119,9 +121,16 @@ io_data_t *io_buffer_create( int is_input )
|
||||
{
|
||||
debug( 1, PIPE_ERROR );
|
||||
wperror( L"fcntl" );
|
||||
return NULL;
|
||||
success = false;
|
||||
}
|
||||
return buffer_redirect.release();
|
||||
|
||||
if (! success)
|
||||
{
|
||||
delete buffer_redirect;
|
||||
buffer_redirect = NULL;
|
||||
}
|
||||
|
||||
return buffer_redirect;
|
||||
}
|
||||
|
||||
void io_buffer_destroy( io_data_t *io_buffer )
|
||||
@@ -145,98 +154,147 @@ void io_buffer_destroy( io_data_t *io_buffer )
|
||||
delete io_buffer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
io_data_t *io_add( io_data_t *list, io_data_t *element )
|
||||
void io_chain_t::remove(const io_data_t *element)
|
||||
{
|
||||
io_data_t *curr = list;
|
||||
if( curr == 0 )
|
||||
return element;
|
||||
while( curr->next != 0 )
|
||||
curr = curr->next;
|
||||
curr->next = element;
|
||||
return list;
|
||||
// See if you can guess why std::find doesn't work here
|
||||
for (io_chain_t::iterator iter = this->begin(); iter != this->end(); ++iter)
|
||||
{
|
||||
if (*iter == element)
|
||||
{
|
||||
this->erase(iter);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
io_data_t *io_remove( io_data_t *list, io_data_t *element )
|
||||
io_chain_t io_chain_t::duplicate() const
|
||||
{
|
||||
io_data_t *curr, *prev=0;
|
||||
for( curr=list; curr; curr = curr->next )
|
||||
{
|
||||
if( element == curr )
|
||||
{
|
||||
if( prev == 0 )
|
||||
{
|
||||
io_data_t *tmp = element->next;
|
||||
element->next = 0;
|
||||
return tmp;
|
||||
}
|
||||
else
|
||||
{
|
||||
prev->next = element->next;
|
||||
element->next = 0;
|
||||
return list;
|
||||
}
|
||||
}
|
||||
prev = curr;
|
||||
}
|
||||
return list;
|
||||
io_chain_t result;
|
||||
result.reserve(this->size());
|
||||
for (io_chain_t::const_iterator iter = this->begin(); iter != this->end(); iter++)
|
||||
{
|
||||
const io_data_t *io = *iter;
|
||||
result.push_back(new io_data_t(*io));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
io_data_t *io_duplicate( io_data_t *l )
|
||||
void io_chain_t::duplicate_prepend(const io_chain_t &src)
|
||||
{
|
||||
io_data_t *res;
|
||||
|
||||
if( l == 0 )
|
||||
return 0;
|
||||
|
||||
res = new io_data_t(*l);
|
||||
res->next=io_duplicate(l->next );
|
||||
return res;
|
||||
/* Prepend a duplicate of src before this. Start by inserting a bunch of NULLs (so we only have to reallocate once) and then replace them. */
|
||||
this->insert(this->begin(), src.size(), NULL);
|
||||
for (size_t idx = 0; idx < src.size(); idx++)
|
||||
{
|
||||
const io_data_t *src_data = src.at(idx);
|
||||
this->at(idx) = new io_data_t(*src_data);
|
||||
}
|
||||
}
|
||||
|
||||
io_data_t *io_get( io_data_t *io, int fd )
|
||||
void io_chain_t::destroy()
|
||||
{
|
||||
if( io == NULL )
|
||||
return 0;
|
||||
|
||||
io_data_t *res = io_get( io->next, fd );
|
||||
if( res )
|
||||
return res;
|
||||
|
||||
if( io->fd == fd )
|
||||
return io;
|
||||
|
||||
return 0;
|
||||
for (size_t idx = 0; idx < this->size(); idx++)
|
||||
{
|
||||
delete this->at(idx);
|
||||
}
|
||||
this->clear();
|
||||
}
|
||||
|
||||
|
||||
void io_print( io_data_t *io )
|
||||
void io_remove(io_chain_t &list, const io_data_t *element)
|
||||
{
|
||||
list.remove(element);
|
||||
}
|
||||
|
||||
io_chain_t io_duplicate(const io_chain_t &chain)
|
||||
{
|
||||
return chain.duplicate();
|
||||
}
|
||||
|
||||
void io_print(const io_chain_t &chain)
|
||||
{
|
||||
if (chain.empty())
|
||||
{
|
||||
fprintf(stderr, "Empty chain %p\n", &chain);
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(stderr, "Chain %p (%ld items):\n", &chain, (long)chain.size());
|
||||
for (size_t i=0; i < chain.size(); i++) {
|
||||
const io_data_t *io = chain.at(i);
|
||||
fprintf(stderr, "\t%lu: fd:%d, input:%s, ", (unsigned long)i, io->fd, io->is_input ? "yes" : "no");
|
||||
switch (io->io_mode)
|
||||
{
|
||||
case IO_FILE:
|
||||
fprintf(stderr, "file (%s)\n", io->filename_cstr);
|
||||
break;
|
||||
case IO_PIPE:
|
||||
fprintf(stderr, "pipe {%d, %d}\n", io->param1.pipe_fd[0], io->param1.pipe_fd[1]);
|
||||
break;
|
||||
case IO_FD:
|
||||
fprintf(stderr, "FD map %d -> %d\n", io->param1.old_fd, io->fd);
|
||||
break;
|
||||
case IO_BUFFER:
|
||||
fprintf(stderr, "buffer %p (size %lu)\n", io->out_buffer_ptr(), io->out_buffer_size());
|
||||
break;
|
||||
case IO_CLOSE:
|
||||
fprintf(stderr, "close %d\n", io->fd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void io_duplicate_prepend( const io_chain_t &src, io_chain_t &dst )
|
||||
{
|
||||
return dst.duplicate_prepend(src);
|
||||
}
|
||||
|
||||
void io_chain_destroy(io_chain_t &chain)
|
||||
{
|
||||
chain.destroy();
|
||||
}
|
||||
|
||||
/* Return the last IO for the given fd */
|
||||
const io_data_t *io_chain_t::get_io_for_fd(int fd) const
|
||||
{
|
||||
size_t idx = this->size();
|
||||
while (idx--)
|
||||
{
|
||||
const io_data_t *data = this->at(idx);
|
||||
if (data->fd == fd) {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
io_data_t *io_chain_t::get_io_for_fd(int fd)
|
||||
{
|
||||
size_t idx = this->size();
|
||||
while (idx--)
|
||||
{
|
||||
io_data_t *data = this->at(idx);
|
||||
if (data->fd == fd) {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* The old function returned the last match, so we mimic that. */
|
||||
const io_data_t *io_chain_get(const io_chain_t &src, int fd)
|
||||
{
|
||||
return src.get_io_for_fd(fd);
|
||||
}
|
||||
|
||||
io_data_t *io_chain_get(io_chain_t &src, int fd)
|
||||
{
|
||||
return src.get_io_for_fd(fd);
|
||||
}
|
||||
|
||||
io_chain_t::io_chain_t(io_data_t *data) : std::vector<io_data_t *>(1, data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
io_chain_t::io_chain_t() : std::vector<io_data_t *>()
|
||||
{
|
||||
if( !io )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
debug( 1, L"IO fd %d, type ", io->fd );
|
||||
switch( io->io_mode )
|
||||
{
|
||||
case IO_PIPE:
|
||||
debug( 1, L"PIPE, data %d", io->param1.pipe_fd[io->fd?1:0] );
|
||||
break;
|
||||
|
||||
case IO_FD:
|
||||
debug( 1, L"FD, copy %d", io->param1.old_fd );
|
||||
break;
|
||||
|
||||
case IO_BUFFER:
|
||||
debug( 1, L"BUFFER" );
|
||||
break;
|
||||
|
||||
default:
|
||||
debug( 1, L"OTHER" );
|
||||
}
|
||||
|
||||
io_print( io->next );
|
||||
|
||||
}
|
||||
|
||||
75
io.h
75
io.h
@@ -21,7 +21,7 @@ class io_data_t
|
||||
shared_ptr<std::vector<char> > out_buffer;
|
||||
|
||||
/* No assignment allowed */
|
||||
void operator=(const io_data_t &rhs) { assert(0); }
|
||||
void operator=(const io_data_t &rhs);
|
||||
|
||||
public:
|
||||
/** Type of redirect */
|
||||
@@ -73,7 +73,12 @@ class io_data_t
|
||||
/** Function to get a pointer to the buffer */
|
||||
char *out_buffer_ptr(void) {
|
||||
assert(out_buffer.get() != NULL);
|
||||
return (out_buffer->size() == 0) ? NULL : &out_buffer->at(0);
|
||||
return out_buffer->empty() ? NULL : &out_buffer->at(0);
|
||||
}
|
||||
|
||||
const char *out_buffer_ptr(void) const {
|
||||
assert(out_buffer.get() != NULL);
|
||||
return out_buffer->empty() ? NULL : &out_buffer->at(0);
|
||||
}
|
||||
|
||||
/** Function to get the size of the buffer */
|
||||
@@ -83,12 +88,16 @@ class io_data_t
|
||||
}
|
||||
|
||||
/** Set to true if this is an input io redirection */
|
||||
int is_input;
|
||||
bool is_input;
|
||||
|
||||
/** Pointer to the next IO redirection */
|
||||
io_data_t *next;
|
||||
|
||||
io_data_t() : filename_cstr(NULL), next(NULL)
|
||||
io_data_t() :
|
||||
out_buffer(),
|
||||
io_mode(0),
|
||||
fd(0),
|
||||
param1(),
|
||||
param2(),
|
||||
filename_cstr(NULL),
|
||||
is_input(0)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -99,10 +108,8 @@ class io_data_t
|
||||
param1(rhs.param1),
|
||||
param2(rhs.param2),
|
||||
filename_cstr(rhs.filename_cstr ? strdup(rhs.filename_cstr) : NULL),
|
||||
is_input(rhs.is_input),
|
||||
next(rhs.next)
|
||||
is_input(rhs.is_input)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
~io_data_t() {
|
||||
@@ -110,27 +117,43 @@ class io_data_t
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
Join two chains of io redirections
|
||||
*/
|
||||
io_data_t *io_add( io_data_t *first_chain, io_data_t *decond_chain );
|
||||
class io_chain_t : public std::vector<io_data_t *> {
|
||||
public:
|
||||
io_chain_t();
|
||||
io_chain_t(io_data_t *);
|
||||
|
||||
void remove(const io_data_t *element);
|
||||
io_chain_t duplicate() const;
|
||||
void duplicate_prepend(const io_chain_t &src);
|
||||
void destroy();
|
||||
|
||||
const io_data_t *get_io_for_fd(int fd) const;
|
||||
io_data_t *get_io_for_fd(int fd);
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
Remove the specified io redirection from the chain
|
||||
*/
|
||||
io_data_t *io_remove( io_data_t *list, io_data_t *element );
|
||||
void io_remove(io_chain_t &list, const io_data_t *element);
|
||||
|
||||
/**
|
||||
Make a copy of the specified chain of redirections. Uses operator new.
|
||||
*/
|
||||
io_data_t *io_duplicate( io_data_t *l );
|
||||
/** Make a copy of the specified chain of redirections. Uses operator new. */
|
||||
io_chain_t io_duplicate(const io_chain_t &chain);
|
||||
|
||||
/** Return a shallow copy of the specified chain of redirections that contains only the applicable redirections. That is, if there's multiple redirections for the same fd, only the second one is included. */
|
||||
io_chain_t io_unique(const io_chain_t &chain);
|
||||
|
||||
/** Prepends a copy of the specified 'src' chain of redirections to 'dst.' Uses operator new. */
|
||||
void io_duplicate_prepend( const io_chain_t &src, io_chain_t &dst );
|
||||
|
||||
/** Destroys an io_chain */
|
||||
void io_chain_destroy(io_chain_t &chain);
|
||||
|
||||
/**
|
||||
Return the last io redirection in the chain for the specified file descriptor.
|
||||
*/
|
||||
io_data_t *io_get( io_data_t *io, int fd );
|
||||
|
||||
const io_data_t *io_chain_get(const io_chain_t &src, int fd);
|
||||
io_data_t *io_chain_get(io_chain_t &src, int fd);
|
||||
|
||||
|
||||
/**
|
||||
@@ -147,16 +170,14 @@ void io_buffer_destroy( io_data_t *io_buffer );
|
||||
used to buffer the output of a command, or non-zero to buffer the
|
||||
input to a command.
|
||||
*/
|
||||
io_data_t *io_buffer_create( int is_input );
|
||||
io_data_t *io_buffer_create( bool is_input );
|
||||
|
||||
/**
|
||||
Close output pipe, and read from input pipe until eof.
|
||||
*/
|
||||
void io_buffer_read( io_data_t *d );
|
||||
|
||||
/**
|
||||
Print debug information about the specified IO redirection chain to stderr.
|
||||
*/
|
||||
void io_print( io_data_t *io );
|
||||
/** Print debug information about the specified IO redirection chain to stderr. */
|
||||
void io_print( const io_chain_t &chain );
|
||||
|
||||
#endif
|
||||
|
||||
21
iothread.cpp
21
iothread.cpp
@@ -96,12 +96,7 @@ static ThreadedRequest_t *dequeue_request(void) {
|
||||
static void *iothread_worker(void *threadPtr) {
|
||||
assert(threadPtr != NULL);
|
||||
struct WorkerThread_t *thread = (struct WorkerThread_t *)threadPtr;
|
||||
|
||||
// We don't want to receive signals on this thread
|
||||
sigset_t set;
|
||||
sigfillset(&set);
|
||||
VOMIT_ON_FAILURE(pthread_sigmask(SIG_SETMASK, &set, NULL));
|
||||
|
||||
|
||||
/* Grab a request off of the queue */
|
||||
struct ThreadedRequest_t *req = dequeue_request();
|
||||
|
||||
@@ -124,7 +119,12 @@ static void iothread_spawn_if_needed(void) {
|
||||
struct WorkerThread_t *thread = next_vacant_thread_slot();
|
||||
assert(thread != NULL);
|
||||
|
||||
/* Spawn a thread */
|
||||
/* The spawned thread inherits our signal mask. We don't want the thread to ever receive signals on the spawned thread, so temporarily block all signals, spawn the thread, and then restore it. */
|
||||
sigset_t newSet, savedSet;
|
||||
sigfillset(&newSet);
|
||||
VOMIT_ON_FAILURE(pthread_sigmask(SIG_BLOCK, &newSet, &savedSet));
|
||||
|
||||
/* Spawn a thread. */
|
||||
int err;
|
||||
do {
|
||||
err = 0;
|
||||
@@ -132,10 +132,15 @@ static void iothread_spawn_if_needed(void) {
|
||||
err = errno;
|
||||
}
|
||||
} while (err == EAGAIN);
|
||||
|
||||
/* Need better error handling - perhaps try again later. */
|
||||
assert(err == 0);
|
||||
|
||||
/* Note that we are spawned another thread */
|
||||
s_active_thread_count += 1;
|
||||
s_active_thread_count += 1;
|
||||
|
||||
/* Restore our sigmask */
|
||||
VOMIT_ON_FAILURE(pthread_sigmask(SIG_SETMASK, &savedSet, NULL));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
11
kill.cpp
11
kill.cpp
@@ -57,14 +57,9 @@ static wchar_t *cut_buffer=0;
|
||||
*/
|
||||
static int has_xsel()
|
||||
{
|
||||
static int called=0;
|
||||
static int res = 0;
|
||||
|
||||
if (!called) {
|
||||
wchar_t *path = path_get_path( L"xsel" );
|
||||
res = !!path;
|
||||
free(path);
|
||||
called = 1;
|
||||
static int res=-1;
|
||||
if (res < 0) {
|
||||
res = !! path_get_path(L"xsel", NULL);
|
||||
}
|
||||
|
||||
return res;
|
||||
|
||||
@@ -43,7 +43,7 @@ def print_completion( cmd, switch_arr, arg, desc ):
|
||||
if len(switch_arr)==0:
|
||||
return
|
||||
|
||||
res = "complete -c %s" % (cmd)
|
||||
res = "complete -c {0}".format(cmd)
|
||||
for sw in switch_arr:
|
||||
|
||||
offset=1
|
||||
@@ -56,9 +56,9 @@ def print_completion( cmd, switch_arr, arg, desc ):
|
||||
switch_type = "l"
|
||||
offset=2
|
||||
|
||||
res += " -%s %s" % (switch_type, escape(sw[offset:]))
|
||||
res += " -{0} {1}".format(switch_type, escape(sw[offset:]))
|
||||
|
||||
res += " --description '%s'" % (up_first(escape_quotes(clean(desc))))
|
||||
res += " --description '{0}'".format(up_first(escape_quotes(clean(desc))))
|
||||
|
||||
print(res)
|
||||
|
||||
@@ -67,9 +67,9 @@ cmd = sys.argv[1]
|
||||
header(cmd)
|
||||
|
||||
try:
|
||||
man = commands.getoutput( "man %s | col -b".format(cmd))
|
||||
man = commands.getoutput( "man {0} | col -b".format(cmd))
|
||||
except NameError:
|
||||
man = subprocess.getoutput( "man %s | col -b".format(cmd))
|
||||
man = subprocess.getoutput( "man {0} | col -b".format(cmd))
|
||||
|
||||
remainder = man
|
||||
|
||||
|
||||
@@ -1033,13 +1033,13 @@ static void write_file( const char *file, int print_path )
|
||||
\param files the list of files for which to perform the action
|
||||
\param fileno an internal value. Should always be set to zero.
|
||||
*/
|
||||
static void launch( char *filter, const string_list_t &files, int fileno )
|
||||
static void launch( char *filter, const string_list_t &files, size_t fileno )
|
||||
{
|
||||
char *filter_org=filter;
|
||||
int count=0;
|
||||
int launch_again=0;
|
||||
|
||||
if( (int)files.size() <= fileno )
|
||||
if( files.size() <= fileno )
|
||||
return;
|
||||
|
||||
|
||||
|
||||
33
osx/Info.plist
Normal file
33
osx/Info.plist
Normal file
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>fish shell</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>fish.icns</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.ridiculousfish.fish-shell</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>fish shell</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>2.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>289.5</string>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.productivity</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>10.6</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2012, ridiculous_fish
|
||||
All rights reserved.</string>
|
||||
<key>LSUIElement</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
221
osx/config.h
Normal file
221
osx/config.h
Normal file
@@ -0,0 +1,221 @@
|
||||
/* config.h. Generated from config.h.in by configure. */
|
||||
/* config.h.in. Generated from configure.ac by autoheader. */
|
||||
|
||||
/* Define to 1 if you have the `backtrace' function. */
|
||||
#define HAVE_BACKTRACE 1
|
||||
|
||||
/* Define to 1 if you have the `backtrace_symbols' function. */
|
||||
#define HAVE_BACKTRACE_SYMBOLS 1
|
||||
|
||||
/* del_curterm is broken, redefine it to a no-op to avoid a double-free bug */
|
||||
/* #undef HAVE_BROKEN_DEL_CURTERM */
|
||||
|
||||
/* Define to 1 one if the implemented fwprintf is broken */
|
||||
/* #undef HAVE_BROKEN_FWPRINTF */
|
||||
|
||||
/* Define to 1 if you have the <curses.h> header file. */
|
||||
#define HAVE_CURSES_H 1
|
||||
|
||||
/* Define to 1 if you have the `dcgettext' function. */
|
||||
/* #undef HAVE_DCGETTEXT */
|
||||
|
||||
/* Define to 1 if you have the <execinfo.h> header file. */
|
||||
#define HAVE_EXECINFO_H 1
|
||||
|
||||
/* Define to 1 if you have the `fgetwc' function. */
|
||||
#define HAVE_FGETWC 1
|
||||
|
||||
/* Define to 1 if you have the `fputwc' function. */
|
||||
#define HAVE_FPUTWC 1
|
||||
|
||||
/* Define to 1 if you have the `futimes' function. */
|
||||
#define HAVE_FUTIMES 1
|
||||
|
||||
/* Define to 1 if you have the `fwprintf' function. */
|
||||
#define HAVE_FWPRINTF 1
|
||||
|
||||
/* Define to 1 if you have the <getopt.h> header file. */
|
||||
#define HAVE_GETOPT_H 1
|
||||
|
||||
/* Define to 1 if you have the `gettext' function. */
|
||||
/* #undef HAVE_GETTEXT */
|
||||
|
||||
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||
#define HAVE_INTTYPES_H 1
|
||||
|
||||
/* Define to 1 if you have the `killpg' function. */
|
||||
#define HAVE_KILLPG 1
|
||||
|
||||
/* Define to 1 if you have the <libintl.h> header file. */
|
||||
/* #undef HAVE_LIBINTL_H */
|
||||
|
||||
/* Define to 1 if you have the `lrand48_r' function. */
|
||||
/* #undef HAVE_LRAND48_R */
|
||||
|
||||
/* Define to 1 if you have the <memory.h> header file. */
|
||||
#define HAVE_MEMORY_H 1
|
||||
|
||||
/* Define to 1 if you have the nan function */
|
||||
#define HAVE_NAN 1
|
||||
|
||||
/* Define to 1 if you have the <ncurses.h> header file. */
|
||||
#define HAVE_NCURSES_H 1
|
||||
|
||||
/* Define to 1 if you have the <ncurses/term.h> header file. */
|
||||
/* #undef HAVE_NCURSES_TERM_H */
|
||||
|
||||
/* Define to 1 if realpath accepts null for its second argument. */
|
||||
#define HAVE_REALPATH_NULL 1
|
||||
|
||||
/* Define to 1 if you have the <regex.h> header file. */
|
||||
#define HAVE_REGEX_H 1
|
||||
|
||||
/* Define to 1 if you have the <siginfo.h> header file. */
|
||||
/* #undef HAVE_SIGINFO_H */
|
||||
|
||||
/* Define to 1 if you have the <spawn.h> header file. */
|
||||
#define HAVE_SPAWN_H 1
|
||||
|
||||
/* Define to 1 if you have the <stdint.h> header file. */
|
||||
#define HAVE_STDINT_H 1
|
||||
|
||||
/* Define to 1 if you have the <stdlib.h> header file. */
|
||||
#define HAVE_STDLIB_H 1
|
||||
|
||||
/* Define to 1 if you have the <strings.h> header file. */
|
||||
#define HAVE_STRINGS_H 1
|
||||
|
||||
/* Define to 1 if you have the <string.h> header file. */
|
||||
#define HAVE_STRING_H 1
|
||||
|
||||
/* Define to 1 if you have the <stropts.h> header file. */
|
||||
/* #undef HAVE_STROPTS_H */
|
||||
|
||||
/* Define to 1 if you have the `sysconf' function. */
|
||||
#define HAVE_SYSCONF 1
|
||||
|
||||
/* Define to 1 if you have the <sys/ioctl.h> header file. */
|
||||
#define HAVE_SYS_IOCTL_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/resource.h> header file. */
|
||||
#define HAVE_SYS_RESOURCE_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/select.h> header file. */
|
||||
#define HAVE_SYS_SELECT_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/stat.h> header file. */
|
||||
#define HAVE_SYS_STAT_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/termios.h> header file. */
|
||||
#define HAVE_SYS_TERMIOS_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/types.h> header file. */
|
||||
#define HAVE_SYS_TYPES_H 1
|
||||
|
||||
/* Define to 1 if you have the <termio.h> header file. */
|
||||
/* #undef HAVE_TERMIO_H */
|
||||
|
||||
/* Define to 1 if you have the <term.h> header file. */
|
||||
#define HAVE_TERM_H 1
|
||||
|
||||
/* Define to 1 if the wgettext function should be used for translating
|
||||
strings. */
|
||||
#define HAVE_TRANSLATE_H 1
|
||||
|
||||
/* Define to 1 if you have the <unistd.h> header file. */
|
||||
#define HAVE_UNISTD_H 1
|
||||
|
||||
/* Define to 1 if you have the `wcscasecmp' function. */
|
||||
#define HAVE_WCSCASECMP 1
|
||||
|
||||
/* Define to 1 if you have the `wcsdup' function. */
|
||||
#define HAVE_WCSDUP 1
|
||||
|
||||
/* Define to 1 if you have the `wcslcat' function. */
|
||||
#define HAVE_WCSLCAT 1
|
||||
|
||||
/* Define to 1 if you have the `wcslcpy' function. */
|
||||
#define HAVE_WCSLCPY 1
|
||||
|
||||
/* Define to 1 if you have the `wcslen' function. */
|
||||
#define HAVE_WCSLEN 1
|
||||
|
||||
/* Define to 1 if you have the `wcsncasecmp' function. */
|
||||
#define HAVE_WCSNCASECMP 1
|
||||
|
||||
/* Define to 1 if you have the `wcsndup' function. */
|
||||
/* #undef HAVE_WCSNDUP */
|
||||
|
||||
/* Define to 1 if you have the `wcstok' function. */
|
||||
#define HAVE_WCSTOK 1
|
||||
|
||||
/* Define to 1 if you have the `wcstol' function. */
|
||||
#define HAVE_WCSTOL 1
|
||||
|
||||
/* Define to 1 if you have the `wcswidth' function. */
|
||||
#define HAVE_WCSWIDTH 1
|
||||
|
||||
/* Define to 1 if you have the `wcwidth' function. */
|
||||
#define HAVE_WCWIDTH 1
|
||||
|
||||
/* Define to 1 if the winsize struct and TIOCGWINSZ macro exist */
|
||||
#define HAVE_WINSIZE 1
|
||||
|
||||
/* Define to 1 if getopt_long exists and works. */
|
||||
#define HAVE_WORKING_GETOPT_LONG 1
|
||||
|
||||
/* Define to 1 if the _nl_msg_cat_cntr symbol is exported. */
|
||||
/* #undef HAVE__NL_MSG_CAT_CNTR */
|
||||
|
||||
/* Define to 1 if you have the file `/proc/self/stat'. */
|
||||
/* #undef HAVE__PROC_SELF_STAT */
|
||||
|
||||
/* Define to 1 if the __environ symbol is exported. */
|
||||
/* #undef HAVE___ENVIRON */
|
||||
|
||||
/* Define to the address where bug reports for this package should be sent. */
|
||||
#define PACKAGE_BUGREPORT "fish-users@lists.sf.net"
|
||||
|
||||
/* Define to the full name of this package. */
|
||||
#define PACKAGE_NAME "fish"
|
||||
|
||||
/* Define to the full name and version of this package. */
|
||||
#define PACKAGE_STRING "fish 2.0.0"
|
||||
|
||||
/* Define to the one symbol short name of this package. */
|
||||
#define PACKAGE_TARNAME "fish"
|
||||
|
||||
/* Define to the home page for this package. */
|
||||
#define PACKAGE_URL ""
|
||||
|
||||
/* Define to the version of this package. */
|
||||
#define PACKAGE_VERSION "2.0.0"
|
||||
|
||||
/* Define to 1 if you have the ANSI C header files. */
|
||||
#define STDC_HEADERS 1
|
||||
|
||||
/* Define to 1 if tparm accepts a fixed amount of paramters. */
|
||||
/* #undef TPARM_SOLARIS_KLUDGE */
|
||||
|
||||
/* Evil kludge to get Power based machines to work */
|
||||
/* #undef TPUTS_KLUDGE */
|
||||
|
||||
/* Perform string translations with gettext */
|
||||
#define USE_GETTEXT 1
|
||||
|
||||
/* Macro to enable additional prototypes under BSD */
|
||||
/* #undef _NETBSD_SOURCE */
|
||||
|
||||
/* Macro to enable additional prototypes under BSD */
|
||||
/* #undef __BSD_VISIBLE */
|
||||
|
||||
/* Macro to enable additional prototypes under Solaris */
|
||||
/* #undef __EXTENSIONS__ */
|
||||
|
||||
#if __GNUC__ >= 3
|
||||
#define __warn_unused __attribute__ ((warn_unused_result))
|
||||
#define __sentinel __attribute__ ((sentinel))
|
||||
#else
|
||||
#define __warn_unused
|
||||
#define __sentinel
|
||||
#endif
|
||||
BIN
osx/launch_fish.scpt
Normal file
BIN
osx/launch_fish.scpt
Normal file
Binary file not shown.
98
osx/osx_fish_launcher.m
Normal file
98
osx/osx_fish_launcher.m
Normal file
@@ -0,0 +1,98 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <CoreServices/CoreServices.h>
|
||||
#import <Carbon/Carbon.h>
|
||||
#import <stdlib.h>
|
||||
#import <stdio.h>
|
||||
#import <unistd.h>
|
||||
#import <errno.h>
|
||||
#import <sys/types.h>
|
||||
#import <sys/stat.h>
|
||||
|
||||
|
||||
// The path to the command file, which we'll delete on death (if it exists)
|
||||
static char s_command_path[PATH_MAX];
|
||||
|
||||
static void die(const char *format, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
vfprintf(stderr, format, ap);
|
||||
va_end(ap);
|
||||
fputc('\n', stderr);
|
||||
|
||||
if (s_command_path[0] != '\0') {
|
||||
unlink(s_command_path);
|
||||
}
|
||||
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static void launch_fish_with_applescript(NSString *fish_binary_path)
|
||||
{
|
||||
// load the script from a resource by fetching its URL from within our bundle
|
||||
NSString *path = [[NSBundle mainBundle] pathForResource:@"launch_fish" ofType:@"scpt"];
|
||||
if (! path) die("Couldn't get path to launch_fish.scpt");
|
||||
|
||||
NSURL *url = [NSURL fileURLWithPath:path isDirectory:NO];
|
||||
if (! url) die("Couldn't get URL to launch_fish.scpt");
|
||||
|
||||
NSDictionary *errors = nil;
|
||||
NSAppleScript *appleScript = [[NSAppleScript alloc] initWithContentsOfURL:url error:&errors];
|
||||
if (! appleScript) die("Couldn't load AppleScript");
|
||||
|
||||
// create the first parameter
|
||||
NSAppleEventDescriptor *firstParameter =
|
||||
[NSAppleEventDescriptor descriptorWithString:fish_binary_path];
|
||||
|
||||
// create and populate the list of parameters (in our case just one)
|
||||
NSAppleEventDescriptor *parameters = [NSAppleEventDescriptor listDescriptor];
|
||||
[parameters insertDescriptor:firstParameter atIndex:1];
|
||||
|
||||
// create the AppleEvent target
|
||||
ProcessSerialNumber psn = {0, kCurrentProcess};
|
||||
NSAppleEventDescriptor *target =
|
||||
[NSAppleEventDescriptor
|
||||
descriptorWithDescriptorType:typeProcessSerialNumber
|
||||
bytes:&psn
|
||||
length:sizeof(ProcessSerialNumber)];
|
||||
|
||||
// create an NSAppleEventDescriptor with the script's method name to call,
|
||||
// this is used for the script statement: "on show_message(user_message)"
|
||||
// Note that the routine name must be in lower case.
|
||||
NSAppleEventDescriptor *handler = [NSAppleEventDescriptor descriptorWithString:
|
||||
[@"launch_fish" lowercaseString]];
|
||||
|
||||
// create the event for an AppleScript subroutine,
|
||||
// set the method name and the list of parameters
|
||||
NSAppleEventDescriptor *event =
|
||||
[NSAppleEventDescriptor appleEventWithEventClass:kASAppleScriptSuite
|
||||
eventID:kASSubroutineEvent
|
||||
targetDescriptor:target
|
||||
returnID:kAutoGenerateReturnID
|
||||
transactionID:kAnyTransactionID];
|
||||
[event setParamDescriptor:handler forKeyword:keyASSubroutineName];
|
||||
[event setParamDescriptor:parameters forKeyword:keyDirectObject];
|
||||
|
||||
// call the event in AppleScript
|
||||
if (![appleScript executeAppleEvent:event error:&errors])
|
||||
{
|
||||
// report any errors from 'errors'
|
||||
NSLog(@"Oops: %@", errors);
|
||||
}
|
||||
|
||||
[appleScript release];
|
||||
}
|
||||
|
||||
/* This approach asks Terminal to open a script that we control */
|
||||
int main(void) {
|
||||
[[NSAutoreleasePool alloc] init];
|
||||
|
||||
/* Get the fish executable. Make sure it's absolute. */
|
||||
NSURL *fish_executable = [[NSBundle mainBundle] URLForResource:@"fish" withExtension:@"" subdirectory:@"base/bin"];
|
||||
if (! fish_executable)
|
||||
die("Could not find fish executable in bundle");
|
||||
|
||||
launch_fish_with_applescript([fish_executable path]);
|
||||
|
||||
/* If we succeeded, it will clean itself up */
|
||||
return 0;
|
||||
}
|
||||
17
output.cpp
17
output.cpp
@@ -161,7 +161,7 @@ static bool write_color(char *todo, unsigned char idx, bool is_fg) {
|
||||
} else {
|
||||
/* We are attempting to bypass the term here. Generate the ANSI escape sequence ourself. */
|
||||
char stridx[128];
|
||||
format_long_safe(stridx, (long)idx);
|
||||
format_long_safe(stridx, idx);
|
||||
char buff[128] = "\x1b[";
|
||||
strcat(buff, is_fg ? "38;5;" : "48;5;");
|
||||
strcat(buff, stridx);
|
||||
@@ -280,7 +280,8 @@ void set_color(rgb_color_t c, rgb_color_t c2)
|
||||
Background is set
|
||||
*/
|
||||
bg_set=1;
|
||||
c = (c2==rgb_color_t::white())?rgb_color_t::black():rgb_color_t::white();
|
||||
if ( c==c2 )
|
||||
c = (c2==rgb_color_t::white())?rgb_color_t::black():rgb_color_t::white();
|
||||
}
|
||||
|
||||
if( (enter_bold_mode != 0) && (strlen(enter_bold_mode) > 0))
|
||||
@@ -506,8 +507,8 @@ void writestr_ellipsis( const wchar_t *str, int max_width )
|
||||
|
||||
while( *str != 0 )
|
||||
{
|
||||
int w = wcwidth( *str );
|
||||
if( written+w+wcwidth( ellipsis_char )>max_width )
|
||||
int w = fish_wcwidth( *str );
|
||||
if( written+w+fish_wcwidth( ellipsis_char )>max_width )
|
||||
{
|
||||
break;
|
||||
}
|
||||
@@ -515,7 +516,7 @@ void writestr_ellipsis( const wchar_t *str, int max_width )
|
||||
writech( *(str++) );
|
||||
}
|
||||
|
||||
written += wcwidth( ellipsis_char );
|
||||
written += fish_wcwidth( ellipsis_char );
|
||||
writech( ellipsis_char );
|
||||
|
||||
while( written < max_width )
|
||||
@@ -540,13 +541,13 @@ int write_escaped_str( const wchar_t *str, int max_len )
|
||||
|
||||
if( max_len && (max_len < len))
|
||||
{
|
||||
for( i=0; (written+wcwidth(out[i]))<=(max_len-1); i++ )
|
||||
for( i=0; (written+fish_wcwidth(out[i]))<=(max_len-1); i++ )
|
||||
{
|
||||
writech( out[i] );
|
||||
written += wcwidth( out[i] );
|
||||
written += fish_wcwidth( out[i] );
|
||||
}
|
||||
writech( ellipsis_char );
|
||||
written += wcwidth( ellipsis_char );
|
||||
written += fish_wcwidth( ellipsis_char );
|
||||
|
||||
for( i=written; i<max_len; i++ )
|
||||
{
|
||||
|
||||
279
parse_util.cpp
279
parse_util.cpp
@@ -51,53 +51,13 @@
|
||||
*/
|
||||
#define AUTOLOAD_MIN_AGE 60
|
||||
|
||||
|
||||
int parse_util_lineno( const wchar_t *str, int len )
|
||||
int parse_util_lineno( const wchar_t *str, size_t offset )
|
||||
{
|
||||
/**
|
||||
First cached state
|
||||
*/
|
||||
static wchar_t *prev_str = 0;
|
||||
static int i=0;
|
||||
static int res = 1;
|
||||
if (! str)
|
||||
return 0;
|
||||
|
||||
/**
|
||||
Second cached state
|
||||
*/
|
||||
static wchar_t *prev_str2 = 0;
|
||||
static int i2 = 0;
|
||||
static int res2 = 1;
|
||||
|
||||
CHECK( str, 0 );
|
||||
|
||||
if( str != prev_str || i>len )
|
||||
{
|
||||
if( prev_str2 == str && i2 <= len )
|
||||
{
|
||||
wchar_t *tmp_str = prev_str;
|
||||
int tmp_i = i;
|
||||
int tmp_res = res;
|
||||
prev_str = prev_str2;
|
||||
i=i2;
|
||||
res=res2;
|
||||
|
||||
prev_str2 = tmp_str;
|
||||
i2 = tmp_i;
|
||||
res2 = tmp_res;
|
||||
}
|
||||
else
|
||||
{
|
||||
prev_str2 = prev_str;
|
||||
i2 = i;
|
||||
res2=res;
|
||||
|
||||
prev_str = (wchar_t *)str;
|
||||
i=0;
|
||||
res=1;
|
||||
}
|
||||
}
|
||||
|
||||
for( ; str[i] && i<len; i++ )
|
||||
int res = 1;
|
||||
for( size_t i=0; str[i] && i<offset; i++ )
|
||||
{
|
||||
if( str[i] == L'\n' )
|
||||
{
|
||||
@@ -108,18 +68,11 @@ int parse_util_lineno( const wchar_t *str, int len )
|
||||
}
|
||||
|
||||
|
||||
int parse_util_get_line_from_offset( const wcstring &str, int pos )
|
||||
int parse_util_get_line_from_offset( const wcstring &str, size_t pos )
|
||||
{
|
||||
// return parse_util_lineno( buff, pos );
|
||||
const wchar_t *buff = str.c_str();
|
||||
int i;
|
||||
int count = 0;
|
||||
if( pos < 0 )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
for( i=0; i<pos; i++ )
|
||||
for( size_t i=0; i<pos; i++ )
|
||||
{
|
||||
if( !buff[i] )
|
||||
{
|
||||
@@ -135,15 +88,15 @@ int parse_util_get_line_from_offset( const wcstring &str, int pos )
|
||||
}
|
||||
|
||||
|
||||
int parse_util_get_offset_from_line( const wcstring &str, int line )
|
||||
size_t parse_util_get_offset_from_line( const wcstring &str, int line )
|
||||
{
|
||||
const wchar_t *buff = str.c_str();
|
||||
int i;
|
||||
size_t i;
|
||||
int count = 0;
|
||||
|
||||
if( line < 0 )
|
||||
{
|
||||
return -1;
|
||||
return (size_t)(-1);
|
||||
}
|
||||
|
||||
if( line == 0 )
|
||||
@@ -161,26 +114,26 @@ int parse_util_get_offset_from_line( const wcstring &str, int line )
|
||||
count++;
|
||||
if( count == line )
|
||||
{
|
||||
return i+1;
|
||||
return (i+1)<str.size()?i+1:i;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int parse_util_get_offset( const wcstring &str, int line, int line_offset )
|
||||
size_t parse_util_get_offset( const wcstring &str, int line, long line_offset )
|
||||
{
|
||||
const wchar_t *buff = str.c_str();
|
||||
int off = parse_util_get_offset_from_line( buff, line );
|
||||
int off2 = parse_util_get_offset_from_line( buff, line+1 );
|
||||
int line_offset2 = line_offset;
|
||||
size_t off = parse_util_get_offset_from_line( buff, line );
|
||||
size_t off2 = parse_util_get_offset_from_line( buff, line+1 );
|
||||
long line_offset2 = line_offset;
|
||||
|
||||
if( off < 0 )
|
||||
if( off == (size_t)(-1) )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if( off2 < 0 )
|
||||
if( off2 == (size_t)(-1) )
|
||||
{
|
||||
off2 = wcslen( buff )+1;
|
||||
}
|
||||
@@ -292,7 +245,7 @@ int parse_util_locate_cmdsubst( const wchar_t *in,
|
||||
|
||||
|
||||
void parse_util_cmdsubst_extent( const wchar_t *buff,
|
||||
int cursor_pos,
|
||||
size_t cursor_pos,
|
||||
const wchar_t **a,
|
||||
const wchar_t **b )
|
||||
{
|
||||
@@ -363,13 +316,13 @@ void parse_util_cmdsubst_extent( const wchar_t *buff,
|
||||
Get the beginning and end of the job or process definition under the cursor
|
||||
*/
|
||||
static void job_or_process_extent( const wchar_t *buff,
|
||||
int cursor_pos,
|
||||
size_t cursor_pos,
|
||||
const wchar_t **a,
|
||||
const wchar_t **b,
|
||||
int process )
|
||||
{
|
||||
const wchar_t *begin, *end;
|
||||
int pos;
|
||||
long pos;
|
||||
wchar_t *buffcpy;
|
||||
int finished=0;
|
||||
|
||||
@@ -460,7 +413,7 @@ static void job_or_process_extent( const wchar_t *buff,
|
||||
}
|
||||
|
||||
void parse_util_process_extent( const wchar_t *buff,
|
||||
int pos,
|
||||
size_t pos,
|
||||
const wchar_t **a,
|
||||
const wchar_t **b )
|
||||
{
|
||||
@@ -468,23 +421,23 @@ void parse_util_process_extent( const wchar_t *buff,
|
||||
}
|
||||
|
||||
void parse_util_job_extent( const wchar_t *buff,
|
||||
int pos,
|
||||
size_t pos,
|
||||
const wchar_t **a,
|
||||
const wchar_t **b )
|
||||
{
|
||||
job_or_process_extent( buff,pos,a, b, 0 );
|
||||
job_or_process_extent( buff,pos,a, b, 0 );
|
||||
}
|
||||
|
||||
|
||||
void parse_util_token_extent( const wchar_t *buff,
|
||||
int cursor_pos,
|
||||
size_t cursor_pos,
|
||||
const wchar_t **tok_begin,
|
||||
const wchar_t **tok_end,
|
||||
const wchar_t **prev_begin,
|
||||
const wchar_t **prev_end )
|
||||
{
|
||||
const wchar_t *begin, *end;
|
||||
int pos;
|
||||
long pos;
|
||||
wchar_t *buffcpy;
|
||||
|
||||
tokenizer tok;
|
||||
@@ -525,15 +478,15 @@ void parse_util_token_extent( const wchar_t *buff,
|
||||
tok_has_next( &tok );
|
||||
tok_next( &tok ) )
|
||||
{
|
||||
int tok_begin = tok_get_pos( &tok );
|
||||
int tok_end=tok_begin;
|
||||
size_t tok_begin = tok_get_pos( &tok );
|
||||
size_t tok_end = tok_begin;
|
||||
|
||||
/*
|
||||
Calculate end of token
|
||||
*/
|
||||
if( tok_last_type( &tok ) == TOK_STRING )
|
||||
{
|
||||
tok_end +=wcslen(tok_last(&tok));
|
||||
tok_end += wcslen(tok_last(&tok));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -660,12 +613,28 @@ wchar_t *parse_util_unescape_wildcards( const wchar_t *str )
|
||||
{
|
||||
case L'\\':
|
||||
{
|
||||
if( *(in+1) )
|
||||
{
|
||||
in++;
|
||||
*(out++)=*in;
|
||||
}
|
||||
*(out++)=*in;
|
||||
switch ( *(in + 1) )
|
||||
{
|
||||
case L'*':
|
||||
case L'?':
|
||||
{
|
||||
in++;
|
||||
*(out++)=*in;
|
||||
break;
|
||||
}
|
||||
case L'\\':
|
||||
{
|
||||
in++;
|
||||
*(out++)=L'\\';
|
||||
*(out++)=L'\\';
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
*(out++)=*in;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -688,6 +657,154 @@ wchar_t *parse_util_unescape_wildcards( const wchar_t *str )
|
||||
}
|
||||
}
|
||||
}
|
||||
*out = *in;
|
||||
return unescaped;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Find the outermost quoting style of current token. Returns 0 if
|
||||
token is not quoted.
|
||||
|
||||
*/
|
||||
static wchar_t get_quote( const wchar_t *cmd, size_t len )
|
||||
{
|
||||
size_t i=0;
|
||||
wchar_t res=0;
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
if( !cmd[i] )
|
||||
break;
|
||||
|
||||
if( cmd[i] == L'\\' )
|
||||
{
|
||||
i++;
|
||||
if( !cmd[i] )
|
||||
break;
|
||||
i++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( cmd[i] == L'\'' || cmd[i] == L'\"' )
|
||||
{
|
||||
const wchar_t *end = quote_end( &cmd[i] );
|
||||
//fwprintf( stderr, L"Jump %d\n", end-cmd );
|
||||
if(( end == 0 ) || (!*end) || (end-cmd > len))
|
||||
{
|
||||
res = cmd[i];
|
||||
break;
|
||||
}
|
||||
i = end-cmd+1;
|
||||
}
|
||||
else
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void parse_util_get_parameter_info( const wcstring &cmd, const size_t pos, wchar_t *quote, size_t *offset, int *type )
|
||||
{
|
||||
size_t prev_pos=0;
|
||||
wchar_t last_quote = '\0';
|
||||
int unfinished;
|
||||
|
||||
tokenizer tok;
|
||||
tok_init( &tok, cmd.c_str(), TOK_ACCEPT_UNFINISHED | TOK_SQUASH_ERRORS );
|
||||
|
||||
for( ; tok_has_next( &tok ); tok_next( &tok ) )
|
||||
{
|
||||
if( tok_get_pos( &tok ) > pos )
|
||||
break;
|
||||
|
||||
if( tok_last_type( &tok ) == TOK_STRING )
|
||||
last_quote = get_quote( tok_last( &tok ),
|
||||
pos - tok_get_pos( &tok ) );
|
||||
|
||||
if( type != NULL )
|
||||
*type = tok_last_type( &tok );
|
||||
|
||||
prev_pos = tok_get_pos( &tok );
|
||||
}
|
||||
|
||||
tok_destroy( &tok );
|
||||
|
||||
wchar_t *cmd_tmp = wcsdup(cmd.c_str());
|
||||
cmd_tmp[pos]=0;
|
||||
size_t cmdlen = wcslen( cmd_tmp );
|
||||
unfinished = (cmdlen==0);
|
||||
if( !unfinished )
|
||||
{
|
||||
unfinished = (quote != 0);
|
||||
|
||||
if( !unfinished )
|
||||
{
|
||||
if( wcschr( L" \t\n\r", cmd_tmp[cmdlen-1] ) != 0 )
|
||||
{
|
||||
if( ( cmdlen == 1) || (cmd_tmp[cmdlen-2] != L'\\') )
|
||||
{
|
||||
unfinished=1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( quote )
|
||||
*quote = last_quote;
|
||||
|
||||
if( offset != 0 )
|
||||
{
|
||||
if( !unfinished )
|
||||
{
|
||||
while( (cmd_tmp[prev_pos] != 0) && (wcschr( L";|",cmd_tmp[prev_pos])!= 0) )
|
||||
prev_pos++;
|
||||
|
||||
*offset = prev_pos;
|
||||
}
|
||||
else
|
||||
{
|
||||
*offset = pos;
|
||||
}
|
||||
}
|
||||
free(cmd_tmp);
|
||||
}
|
||||
|
||||
wcstring parse_util_escape_string_with_quote( const wcstring &cmd, wchar_t quote)
|
||||
{
|
||||
wcstring result;
|
||||
if( quote == L'\0' )
|
||||
{
|
||||
result = escape_string( cmd, ESCAPE_ALL | ESCAPE_NO_QUOTED | ESCAPE_NO_TILDE );
|
||||
}
|
||||
else
|
||||
{
|
||||
bool unescapable = false;
|
||||
for (size_t i = 0; i < cmd.size(); i++)
|
||||
{
|
||||
wchar_t c = cmd.at(i);
|
||||
switch (c)
|
||||
{
|
||||
case L'\n':
|
||||
case L'\t':
|
||||
case L'\b':
|
||||
case L'\r':
|
||||
unescapable = true;
|
||||
break;
|
||||
default:
|
||||
if (c == quote)
|
||||
result.push_back(L'\\');
|
||||
result.push_back(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (unescapable)
|
||||
{
|
||||
result = escape_string(cmd, ESCAPE_ALL | ESCAPE_NO_QUOTED);
|
||||
result.insert(0, "e, 1);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
31
parse_util.h
31
parse_util.h
@@ -40,7 +40,7 @@ int parse_util_locate_cmdsubst( const wchar_t *in,
|
||||
\param b the end of the searched string
|
||||
*/
|
||||
void parse_util_cmdsubst_extent( const wchar_t *buff,
|
||||
int cursor_pos,
|
||||
size_t cursor_pos,
|
||||
const wchar_t **a,
|
||||
const wchar_t **b );
|
||||
|
||||
@@ -53,7 +53,7 @@ void parse_util_cmdsubst_extent( const wchar_t *buff,
|
||||
\param b the end of the searched string
|
||||
*/
|
||||
void parse_util_process_extent( const wchar_t *buff,
|
||||
int cursor_pos,
|
||||
size_t cursor_pos,
|
||||
const wchar_t **a,
|
||||
const wchar_t **b );
|
||||
|
||||
@@ -67,7 +67,7 @@ void parse_util_process_extent( const wchar_t *buff,
|
||||
\param b the end of the searched string
|
||||
*/
|
||||
void parse_util_job_extent( const wchar_t *buff,
|
||||
int cursor_pos,
|
||||
size_t cursor_pos,
|
||||
const wchar_t **a,
|
||||
const wchar_t **b );
|
||||
|
||||
@@ -84,7 +84,7 @@ void parse_util_job_extent( const wchar_t *buff,
|
||||
\param prev_end the end of the token before the current token
|
||||
*/
|
||||
void parse_util_token_extent( const wchar_t *buff,
|
||||
int cursor_pos,
|
||||
size_t cursor_pos,
|
||||
const wchar_t **tok_begin,
|
||||
const wchar_t **tok_end,
|
||||
const wchar_t **prev_begin,
|
||||
@@ -94,23 +94,23 @@ void parse_util_token_extent( const wchar_t *buff,
|
||||
/**
|
||||
Get the linenumber at the specified character offset
|
||||
*/
|
||||
int parse_util_lineno( const wchar_t *str, int len );
|
||||
int parse_util_lineno( const wchar_t *str, size_t len );
|
||||
|
||||
/**
|
||||
Calculate the line number of the specified cursor position
|
||||
*/
|
||||
int parse_util_get_line_from_offset( const wcstring &str, int pos );
|
||||
int parse_util_get_line_from_offset( const wcstring &str, size_t pos );
|
||||
|
||||
/**
|
||||
Get the offset of the first character on the specified line
|
||||
*/
|
||||
int parse_util_get_offset_from_line( const wcstring &str, int line );
|
||||
size_t parse_util_get_offset_from_line( const wcstring &str, int line );
|
||||
|
||||
|
||||
/**
|
||||
Return the total offset of the buffer for the cursor position nearest to the specified poition
|
||||
*/
|
||||
int parse_util_get_offset( const wcstring &str, int line, int line_offset );
|
||||
size_t parse_util_get_offset( const wcstring &str, int line, long line_offset );
|
||||
|
||||
/**
|
||||
Set the argv environment variable to the specified null-terminated
|
||||
@@ -124,6 +124,21 @@ void parse_util_set_argv( const wchar_t * const *argv, const wcstring_list_t &na
|
||||
*/
|
||||
wchar_t *parse_util_unescape_wildcards( const wchar_t *in );
|
||||
|
||||
/**
|
||||
Calculates information on the parameter at the specified index.
|
||||
|
||||
\param cmd The command to be analyzed
|
||||
\param pos An index in the string which is inside the parameter
|
||||
\param quote If not NULL, store the type of quote this parameter has, can be either ', " or \\0, meaning the string is not quoted.
|
||||
\param offset If not NULL, get_param will store the offset to the beginning of the parameter.
|
||||
\param type If not NULL, get_param will store the token type as returned by tok_last.
|
||||
*/
|
||||
void parse_util_get_parameter_info( const wcstring &cmd, const size_t pos, wchar_t *quote, size_t *offset, int *type );
|
||||
|
||||
/**
|
||||
Attempts to escape the string 'cmd' using the given quote type, as determined by the quote character. The quote can be a single quote or double quote, or L'\0' to indicate no quoting (and thus escaping should be with backslashes).
|
||||
*/
|
||||
wcstring parse_util_escape_string_with_quote( const wcstring &cmd, wchar_t quote);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
628
parser.cpp
628
parser.cpp
File diff suppressed because it is too large
Load Diff
284
parser.h
284
parser.h
@@ -12,15 +12,14 @@
|
||||
#include "event.h"
|
||||
#include "function.h"
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
#define PARSER_TEST_ERROR 1
|
||||
#define PARSER_TEST_INCOMPLETE 2
|
||||
|
||||
/**
|
||||
event_block_t represents a block on events of the specified type
|
||||
event_blockage_t represents a block on events of the specified type
|
||||
*/
|
||||
struct event_block_t
|
||||
struct event_blockage_t
|
||||
{
|
||||
/**
|
||||
The types of events to block. This is interpreted as a bitset
|
||||
@@ -33,10 +32,10 @@ struct event_block_t
|
||||
unsigned int typemask;
|
||||
};
|
||||
|
||||
typedef std::list<event_block_t> event_block_list_t;
|
||||
typedef std::list<event_blockage_t> event_blockage_list_t;
|
||||
|
||||
inline bool event_block_list_blocks_type(const event_block_list_t &ebls, int type) {
|
||||
for (event_block_list_t::const_iterator iter = ebls.begin(); iter != ebls.end(); ++iter) {
|
||||
inline bool event_block_list_blocks_type(const event_blockage_list_t &ebls, int type) {
|
||||
for (event_blockage_list_t::const_iterator iter = ebls.begin(); iter != ebls.end(); ++iter) {
|
||||
if( iter->typemask & (1<<EVENT_ANY ) )
|
||||
return true;
|
||||
if( iter->typemask & (1<<type) )
|
||||
@@ -46,122 +45,6 @@ inline bool event_block_list_blocks_type(const event_block_list_t &ebls, int typ
|
||||
}
|
||||
|
||||
|
||||
/** Block state template, to replace the discriminated union */
|
||||
struct block_state_base_t {
|
||||
public:
|
||||
virtual ~block_state_base_t() {}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct block_state_t : public block_state_base_t {
|
||||
T value;
|
||||
block_state_t() : value() {}
|
||||
};
|
||||
|
||||
/**
|
||||
block_t represents a block of commands.
|
||||
*/
|
||||
typedef struct block
|
||||
{
|
||||
int type; /**< Type of block. Can be one of WHILE, FOR, IF and FUNCTION */
|
||||
int skip; /**< Whether execution of the commands in this block should be skipped */
|
||||
int tok_pos; /**< The start index of the block */
|
||||
int had_command; /**< Set to non-zero once a command has been executed in this block */
|
||||
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
job_t *job;
|
||||
|
||||
/**
|
||||
Block type-specific data
|
||||
*/
|
||||
std::auto_ptr<function_data_t> function_data;
|
||||
|
||||
#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
|
||||
|
||||
/** First block type specific variable */
|
||||
block_state_base_t *state1_ptr;
|
||||
|
||||
template<typename T>
|
||||
T& state1(void) {
|
||||
block_state_t<T> *state;
|
||||
if (state1_ptr == NULL) {
|
||||
state = new block_state_t<T>();
|
||||
state1_ptr = state;
|
||||
} else {
|
||||
state = dynamic_cast<block_state_t<T> *>(state1_ptr);
|
||||
if (state == NULL) {
|
||||
printf("Expected type %s, but instead got type %s\n", typeid(T).name(), typeid(*state1_ptr).name());
|
||||
abort();
|
||||
}
|
||||
}
|
||||
return state->value;
|
||||
}
|
||||
|
||||
/** Second block type specific variable */
|
||||
block_state_base_t *state2_ptr;
|
||||
|
||||
template<typename T>
|
||||
T& state2(void) {
|
||||
block_state_t<T> *state;
|
||||
if (state2_ptr == NULL) {
|
||||
state = new block_state_t<T>();
|
||||
state2_ptr = state;
|
||||
} else {
|
||||
state = dynamic_cast<block_state_t<T> *>(state2_ptr);
|
||||
assert(state != NULL);
|
||||
}
|
||||
return state->value;
|
||||
}
|
||||
|
||||
/**
|
||||
Name of file that created this block
|
||||
*/
|
||||
const wchar_t *src_filename;
|
||||
|
||||
/**
|
||||
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 */
|
||||
bool wants_pop_env;
|
||||
|
||||
/** List of event blocks. */
|
||||
event_block_list_t event_blocks;
|
||||
|
||||
/**
|
||||
Next outer block
|
||||
*/
|
||||
struct block *outer;
|
||||
|
||||
/** Destructor */
|
||||
~block()
|
||||
{
|
||||
if (state1_ptr != NULL)
|
||||
delete state1_ptr;
|
||||
if (state2_ptr != NULL)
|
||||
delete state2_ptr;
|
||||
}
|
||||
} block_t;
|
||||
|
||||
/**
|
||||
Types of blocks
|
||||
*/
|
||||
@@ -184,6 +67,147 @@ enum block_type_t
|
||||
}
|
||||
;
|
||||
|
||||
/**
|
||||
block_t represents a block of commands.
|
||||
*/
|
||||
struct block_t
|
||||
{
|
||||
protected:
|
||||
/** Protected constructor. Use one of the subclasses below. */
|
||||
block_t(block_type_t t);
|
||||
|
||||
private:
|
||||
const block_type_t block_type; /**< Type of block. */
|
||||
bool made_fake;
|
||||
|
||||
public:
|
||||
block_type_t type() const { return this->made_fake ? FAKE : this->block_type; }
|
||||
|
||||
/** Mark a block as fake; this is used by the return statement. */
|
||||
void mark_as_fake() { this->made_fake = true; }
|
||||
|
||||
bool skip; /**< Whether execution of the commands in this block should be skipped */
|
||||
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.
|
||||
*/
|
||||
int loop_status;
|
||||
|
||||
/**
|
||||
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
|
||||
*/
|
||||
const wchar_t *src_filename;
|
||||
|
||||
/**
|
||||
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 */
|
||||
bool wants_pop_env;
|
||||
|
||||
/** List of event blocks. */
|
||||
event_blockage_list_t event_blocks;
|
||||
|
||||
/**
|
||||
Next outer block
|
||||
*/
|
||||
block_t *outer;
|
||||
|
||||
/** Destructor */
|
||||
virtual ~block_t();
|
||||
};
|
||||
|
||||
struct if_block_t : public block_t
|
||||
{
|
||||
bool if_expr_evaluated; // whether we've evaluated the if expression
|
||||
bool is_elseif_entry; // whether we're at the beginning of an ELSEIF branch
|
||||
bool any_branch_taken; // whether the clause of the if statement or any elseif has been found to be true
|
||||
bool else_evaluated; // whether we've encountered a terminal else block
|
||||
|
||||
if_block_t();
|
||||
};
|
||||
|
||||
struct event_block_t : public block_t
|
||||
{
|
||||
const event_t * const event;
|
||||
event_block_t(const event_t *evt);
|
||||
};
|
||||
|
||||
struct function_block_t : public block_t
|
||||
{
|
||||
process_t *process;
|
||||
wcstring name;
|
||||
function_block_t(process_t *p, const wcstring &n, bool shadows);
|
||||
};
|
||||
|
||||
struct source_block_t : public block_t
|
||||
{
|
||||
const wchar_t * const source_file;
|
||||
source_block_t(const wchar_t *src);
|
||||
};
|
||||
|
||||
struct for_block_t : public block_t
|
||||
{
|
||||
wcstring variable; // the variable that will be assigned each value in the sequence
|
||||
wcstring_list_t sequence; // the sequence of values
|
||||
for_block_t(const wcstring &var);
|
||||
};
|
||||
|
||||
struct while_block_t : public block_t
|
||||
{
|
||||
int status;
|
||||
while_block_t();
|
||||
};
|
||||
|
||||
struct switch_block_t : public block_t
|
||||
{
|
||||
bool switch_taken;
|
||||
const wcstring switch_value;
|
||||
switch_block_t(const wcstring &sv);
|
||||
};
|
||||
|
||||
struct fake_block_t : public block_t
|
||||
{
|
||||
fake_block_t();
|
||||
};
|
||||
|
||||
struct function_def_block_t : public block_t
|
||||
{
|
||||
function_data_t function_data;
|
||||
function_def_block_t();
|
||||
};
|
||||
|
||||
struct scope_block_t : public block_t
|
||||
{
|
||||
scope_block_t(block_type_t type); //must be BEGIN, TOP or SUBST
|
||||
};
|
||||
|
||||
struct breakpoint_block_t : public block_t
|
||||
{
|
||||
breakpoint_block_t();
|
||||
};
|
||||
|
||||
/**
|
||||
Possible states for a loop
|
||||
*/
|
||||
@@ -207,7 +231,6 @@ enum while_status
|
||||
;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
Errors that can be generated by the parser
|
||||
*/
|
||||
@@ -310,7 +333,7 @@ class parser_t {
|
||||
parser_t(const parser_t&);
|
||||
parser_t& operator=(const parser_t&);
|
||||
|
||||
void parse_job_argument_list( process_t *p, job_t *j, tokenizer *tok, std::vector<completion_t>& );
|
||||
void parse_job_argument_list( process_t *p, job_t *j, tokenizer *tok, std::vector<completion_t>&, bool );
|
||||
int parse_job( process_t *p, job_t *j, tokenizer *tok );
|
||||
void skipped_exec( job_t * j );
|
||||
void eval_job( tokenizer *tok );
|
||||
@@ -344,10 +367,10 @@ class parser_t {
|
||||
block_t *current_block;
|
||||
|
||||
/** Global event blocks */
|
||||
event_block_list_t global_event_blocks;
|
||||
event_blockage_list_t global_event_blocks;
|
||||
|
||||
/** Current block level io redirections */
|
||||
io_data_t *block_io;
|
||||
io_chain_t block_io;
|
||||
|
||||
/**
|
||||
Evaluate the expressions contained in cmd.
|
||||
@@ -358,7 +381,7 @@ class parser_t {
|
||||
|
||||
\return 0 on success, 1 otherwise
|
||||
*/
|
||||
int eval( const wcstring &cmd, io_data_t *io, enum block_type_t block_type );
|
||||
int eval( const wcstring &cmdStr, 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.
|
||||
@@ -392,6 +415,9 @@ class parser_t {
|
||||
|
||||
/** Returns the current line number */
|
||||
int get_lineno() const;
|
||||
|
||||
/** Returns the line number for the character at the given index */
|
||||
int line_number_of_character_at_offset(size_t idx) const;
|
||||
|
||||
/** Returns the current position in the latest string of the tokenizer. */
|
||||
int get_pos() const;
|
||||
@@ -408,8 +434,8 @@ class parser_t {
|
||||
/** Get the list of jobs */
|
||||
job_list_t &job_list() { return my_job_list; }
|
||||
|
||||
/** Create block of specified type */
|
||||
void push_block( int type);
|
||||
/** Pushes the block. pop_block will call delete on it. */
|
||||
void push_block( block_t *newv );
|
||||
|
||||
/** Remove the outermost block namespace */
|
||||
void pop_block();
|
||||
|
||||
@@ -26,7 +26,7 @@ bool parser_keywords_is_switch( const wcstring &cmd );
|
||||
|
||||
|
||||
/**
|
||||
Tests if the specified commands parameters should be interpreted as another command, which will be true if the command is either 'command', 'exec', 'if', 'while' or 'builtin'.
|
||||
Tests if the specified commands parameters should be interpreted as another command, which will be true if the command is either 'command', 'exec', 'if', 'while', or 'builtin'. This does not handle "else if" which is more complicated.
|
||||
|
||||
\param cmd The command name to test
|
||||
\return 1 of the command parameter is a command, 0 otherwise
|
||||
|
||||
279
path.cpp
279
path.cpp
@@ -23,13 +23,14 @@
|
||||
*/
|
||||
#define MISSING_COMMAND_ERR_MSG _( L"Error while searching for command '%ls'" )
|
||||
|
||||
bool path_get_path_string(const wcstring &cmd_str, wcstring &output, const env_vars &vars)
|
||||
static bool path_get_path_core(const wcstring &cmd, wcstring *out_path, const env_var_t &bin_path_var)
|
||||
{
|
||||
const wchar_t * const cmd = cmd_str.c_str();
|
||||
int err = ENOENT;
|
||||
debug( 3, L"path_get_path_string( '%ls' )", cmd );
|
||||
int err = ENOENT;
|
||||
|
||||
debug( 3, L"path_get_path( '%ls' )", cmd.c_str() );
|
||||
|
||||
if(wcschr( cmd, L'/' ) != 0 )
|
||||
/* If the command has a slash, it must be a full path */
|
||||
if (cmd.find(L'/') != wcstring::npos)
|
||||
{
|
||||
if( waccess( cmd, X_OK )==0 )
|
||||
{
|
||||
@@ -39,12 +40,13 @@ bool path_get_path_string(const wcstring &cmd_str, wcstring &output, const env_v
|
||||
return false;
|
||||
}
|
||||
|
||||
if (S_ISREG(buff.st_mode))
|
||||
if( S_ISREG(buff.st_mode) )
|
||||
{
|
||||
output = cmd_str;
|
||||
if (out_path)
|
||||
out_path->assign(cmd);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
errno = EACCES;
|
||||
return false;
|
||||
@@ -52,39 +54,42 @@ bool path_get_path_string(const wcstring &cmd_str, wcstring &output, const env_v
|
||||
}
|
||||
else
|
||||
{
|
||||
//struct stat buff;
|
||||
//wstat( cmd, &buff );
|
||||
struct stat buff;
|
||||
wstat( cmd, &buff );
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
const wchar_t *path = vars.get(L"PATH");
|
||||
if( path == 0 )
|
||||
wcstring bin_path;
|
||||
if (! bin_path_var.missing())
|
||||
{
|
||||
bin_path = bin_path_var;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( contains( PREFIX L"/bin", L"/bin", L"/usr/bin" ) )
|
||||
if (contains( PREFIX L"/bin", L"/bin", L"/usr/bin" ))
|
||||
{
|
||||
path = L"/bin" ARRAY_SEP_STR L"/usr/bin";
|
||||
bin_path = L"/bin" ARRAY_SEP_STR L"/usr/bin";
|
||||
}
|
||||
else
|
||||
{
|
||||
path = L"/bin" ARRAY_SEP_STR L"/usr/bin" ARRAY_SEP_STR PREFIX L"/bin";
|
||||
bin_path = L"/bin" ARRAY_SEP_STR L"/usr/bin" ARRAY_SEP_STR PREFIX L"/bin";
|
||||
}
|
||||
}
|
||||
|
||||
wcstokenizer tokenizer(path, ARRAY_SEP_STR);
|
||||
wcstring new_cmd;
|
||||
while (tokenizer.next(new_cmd))
|
||||
{
|
||||
size_t path_len = new_cmd.size();
|
||||
if (path_len == 0) continue;
|
||||
|
||||
append_path_component(new_cmd, cmd_str);
|
||||
if( waccess( new_cmd, X_OK )==0 )
|
||||
wcstring nxt_path;
|
||||
wcstokenizer tokenizer(bin_path, ARRAY_SEP_STR);
|
||||
while (tokenizer.next(nxt_path))
|
||||
{
|
||||
if (nxt_path.empty())
|
||||
continue;
|
||||
append_path_component(nxt_path, cmd);
|
||||
if( waccess( nxt_path, X_OK )==0 )
|
||||
{
|
||||
struct stat buff;
|
||||
if( wstat( new_cmd, &buff )==-1 )
|
||||
if( wstat( nxt_path, &buff )==-1 )
|
||||
{
|
||||
if( errno != EACCES )
|
||||
{
|
||||
@@ -94,8 +99,9 @@ bool path_get_path_string(const wcstring &cmd_str, wcstring &output, const env_v
|
||||
}
|
||||
if( S_ISREG(buff.st_mode) )
|
||||
{
|
||||
output = new_cmd;
|
||||
return true;
|
||||
if (out_path)
|
||||
out_path->swap(nxt_path);
|
||||
return true;
|
||||
}
|
||||
err = EACCES;
|
||||
|
||||
@@ -113,7 +119,7 @@ bool path_get_path_string(const wcstring &cmd_str, wcstring &output, const env_v
|
||||
{
|
||||
debug( 1,
|
||||
MISSING_COMMAND_ERR_MSG,
|
||||
new_cmd.c_str() );
|
||||
nxt_path.c_str() );
|
||||
wperror( L"access" );
|
||||
}
|
||||
}
|
||||
@@ -123,147 +129,19 @@ bool path_get_path_string(const wcstring &cmd_str, wcstring &output, const env_v
|
||||
|
||||
errno = err;
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
wchar_t *path_get_path( const wchar_t *cmd )
|
||||
bool path_get_path(const wcstring &cmd, wcstring *out_path, const env_vars_snapshot_t &vars)
|
||||
{
|
||||
int err = ENOENT;
|
||||
|
||||
CHECK( cmd, 0 );
|
||||
|
||||
debug( 3, L"path_get_path( '%ls' )", cmd );
|
||||
|
||||
if(wcschr( cmd, L'/' ) != 0 )
|
||||
{
|
||||
if( waccess( cmd, X_OK )==0 )
|
||||
{
|
||||
struct stat buff;
|
||||
if(wstat( cmd, &buff ))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( S_ISREG(buff.st_mode) )
|
||||
return wcsdup( cmd );
|
||||
else
|
||||
{
|
||||
errno = EACCES;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
struct stat buff;
|
||||
wstat( cmd, &buff );
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
env_var_t path = env_get_string(L"PATH");
|
||||
if( path.missing() )
|
||||
{
|
||||
if( contains( PREFIX L"/bin", L"/bin", L"/usr/bin" ) )
|
||||
{
|
||||
path = L"/bin" ARRAY_SEP_STR L"/usr/bin";
|
||||
}
|
||||
else
|
||||
{
|
||||
path = L"/bin" ARRAY_SEP_STR L"/usr/bin" ARRAY_SEP_STR PREFIX L"/bin";
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Allocate string long enough to hold the whole command
|
||||
*/
|
||||
wchar_t *new_cmd = (wchar_t *)calloc(wcslen(cmd)+path.size()+2, sizeof(wchar_t) );
|
||||
|
||||
/*
|
||||
We tokenize a copy of the path, since strtok modifies
|
||||
its arguments
|
||||
*/
|
||||
wchar_t *path_cpy = wcsdup( path.c_str() );
|
||||
wchar_t *state;
|
||||
|
||||
if( (new_cmd==0) || (path_cpy==0) )
|
||||
{
|
||||
DIE_MEM();
|
||||
}
|
||||
|
||||
for( const wchar_t *nxt_path = wcstok( path_cpy, ARRAY_SEP_STR, &state );
|
||||
nxt_path != 0;
|
||||
nxt_path = wcstok( 0, ARRAY_SEP_STR, &state) )
|
||||
{
|
||||
int path_len = wcslen( nxt_path );
|
||||
wcscpy( new_cmd, nxt_path );
|
||||
if( new_cmd[path_len-1] != L'/' )
|
||||
{
|
||||
new_cmd[path_len++]=L'/';
|
||||
}
|
||||
wcscpy( &new_cmd[path_len], cmd );
|
||||
if( waccess( new_cmd, X_OK )==0 )
|
||||
{
|
||||
struct stat buff;
|
||||
if( wstat( new_cmd, &buff )==-1 )
|
||||
{
|
||||
if( errno != EACCES )
|
||||
{
|
||||
wperror( L"stat" );
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if( S_ISREG(buff.st_mode) )
|
||||
{
|
||||
free( path_cpy );
|
||||
return new_cmd;
|
||||
}
|
||||
err = EACCES;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
switch( errno )
|
||||
{
|
||||
case ENOENT:
|
||||
case ENAMETOOLONG:
|
||||
case EACCES:
|
||||
case ENOTDIR:
|
||||
break;
|
||||
default:
|
||||
{
|
||||
debug( 1,
|
||||
MISSING_COMMAND_ERR_MSG,
|
||||
new_cmd );
|
||||
wperror( L"access" );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
free( new_cmd );
|
||||
free( path_cpy );
|
||||
|
||||
}
|
||||
|
||||
errno = err;
|
||||
return 0;
|
||||
return path_get_path_core(cmd, out_path, vars.get(L"PATH"));
|
||||
}
|
||||
|
||||
bool path_get_path_string(const wcstring &cmd, wcstring &output)
|
||||
bool path_get_path(const wcstring &cmd, wcstring *out_path)
|
||||
{
|
||||
bool success = false;
|
||||
wchar_t *tmp = path_get_path(cmd.c_str());
|
||||
if (tmp) {
|
||||
output = tmp;
|
||||
free(tmp);
|
||||
success = true;
|
||||
}
|
||||
return success;
|
||||
return path_get_path_core(cmd, out_path, env_get_string(L"PATH"));
|
||||
}
|
||||
|
||||
|
||||
bool path_get_cdpath_string(const wcstring &dir_str, wcstring &result, const env_vars &vars)
|
||||
bool path_get_cdpath_string(const wcstring &dir_str, wcstring &result, const env_var_t &cdpath)
|
||||
{
|
||||
wchar_t *res = 0;
|
||||
int err = ENOENT;
|
||||
@@ -290,13 +168,12 @@ bool path_get_cdpath_string(const wcstring &dir_str, wcstring &result, const env
|
||||
else
|
||||
{
|
||||
|
||||
const wchar_t *path = L".";
|
||||
wcstring path = L".";
|
||||
|
||||
// Respect CDPATH
|
||||
env_var_t cdpath = env_get_string(L"CDPATH");
|
||||
if (! cdpath.missing_or_empty()) {
|
||||
path = cdpath.c_str();
|
||||
printf("CDPATH: %ls\n", path);
|
||||
}
|
||||
|
||||
wcstokenizer tokenizer(path, ARRAY_SEP_STR);
|
||||
@@ -342,21 +219,19 @@ bool path_get_cdpath_string(const wcstring &dir_str, wcstring &result, const env
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
wchar_t *path_allocate_cdpath( const wcstring &dir, const wchar_t *wd )
|
||||
bool path_get_cdpath(const wcstring &dir, wcstring *out, const wchar_t *wd, const env_vars_snapshot_t &env_vars)
|
||||
{
|
||||
wchar_t *res = NULL;
|
||||
int err = ENOENT;
|
||||
if (dir.empty())
|
||||
return NULL;
|
||||
return false;
|
||||
|
||||
if (wd) {
|
||||
if (wd)
|
||||
{
|
||||
size_t len = wcslen(wd);
|
||||
assert(wd[len - 1] == L'/');
|
||||
}
|
||||
|
||||
wcstring_list_t paths;
|
||||
|
||||
if (dir.at(0) == L'/') {
|
||||
/* Absolute path */
|
||||
paths.push_back(dir);
|
||||
@@ -370,41 +245,35 @@ wchar_t *path_allocate_cdpath( const wcstring &dir, const wchar_t *wd )
|
||||
path.append(dir);
|
||||
paths.push_back(path);
|
||||
} else {
|
||||
wchar_t *path_cpy;
|
||||
wchar_t *state;
|
||||
|
||||
// Respect CDPATH
|
||||
env_var_t path = env_get_string(L"CDPATH");
|
||||
if (path.missing_or_empty()) path = L"."; //We'll change this to the wd if we have one
|
||||
env_var_t path = env_vars.get(L"CDPATH");
|
||||
if (path.missing_or_empty())
|
||||
path = L"."; //We'll change this to the wd if we have one
|
||||
|
||||
path_cpy = wcsdup( path.c_str() );
|
||||
|
||||
for( const wchar_t *nxt_path = wcstok( path_cpy, ARRAY_SEP_STR, &state );
|
||||
nxt_path != NULL;
|
||||
nxt_path = wcstok( 0, ARRAY_SEP_STR, &state) )
|
||||
wcstring nxt_path;
|
||||
wcstokenizer tokenizer(path, ARRAY_SEP_STR);
|
||||
while (tokenizer.next(nxt_path))
|
||||
{
|
||||
|
||||
if (! wcscmp(nxt_path, L".") && wd != NULL) {
|
||||
if (nxt_path == L"." && wd != NULL) {
|
||||
// nxt_path is just '.', and we have a working directory, so use the wd instead
|
||||
// TODO: if nxt_path starts with ./ we need to replace the . with the wd
|
||||
nxt_path = wd;
|
||||
}
|
||||
|
||||
wcstring expanded_path = nxt_path;
|
||||
expand_tilde(expanded_path);
|
||||
expand_tilde(nxt_path);
|
||||
|
||||
// debug( 2, L"woot %ls\n", expanded_path.c_str() );
|
||||
|
||||
if (expanded_path.empty())
|
||||
if (nxt_path.empty())
|
||||
continue;
|
||||
|
||||
wcstring whole_path = expanded_path;
|
||||
wcstring whole_path = nxt_path;
|
||||
append_path_component(whole_path, dir);
|
||||
paths.push_back(whole_path);
|
||||
}
|
||||
free( path_cpy );
|
||||
}
|
||||
|
||||
bool success = false;
|
||||
for (wcstring_list_t::const_iterator iter = paths.begin(); iter != paths.end(); ++iter) {
|
||||
struct stat buf;
|
||||
const wcstring &dir = *iter;
|
||||
@@ -412,7 +281,9 @@ wchar_t *path_allocate_cdpath( const wcstring &dir, const wchar_t *wd )
|
||||
{
|
||||
if( S_ISDIR(buf.st_mode) )
|
||||
{
|
||||
res = wcsdup(dir.c_str());
|
||||
success = true;
|
||||
if (out)
|
||||
out->assign(dir);
|
||||
break;
|
||||
}
|
||||
else
|
||||
@@ -422,24 +293,12 @@ wchar_t *path_allocate_cdpath( const wcstring &dir, const wchar_t *wd )
|
||||
}
|
||||
}
|
||||
|
||||
if( !res )
|
||||
{
|
||||
if (! success)
|
||||
errno = err;
|
||||
}
|
||||
|
||||
return res;
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
bool path_can_get_cdpath(const wcstring &in, const wchar_t *wd)
|
||||
{
|
||||
wchar_t *tmp = path_allocate_cdpath(in, wd);
|
||||
bool result = (tmp != NULL);
|
||||
free(tmp);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool path_can_be_implicit_cd(const wcstring &path, wcstring *out_path, const wchar_t *wd)
|
||||
bool path_can_be_implicit_cd(const wcstring &path, wcstring *out_path, const wchar_t *wd, const env_vars_snapshot_t &vars)
|
||||
{
|
||||
wcstring exp_path = path;
|
||||
expand_tilde(exp_path);
|
||||
@@ -450,16 +309,8 @@ bool path_can_be_implicit_cd(const wcstring &path, wcstring *out_path, const wch
|
||||
string_prefixes_string(L"../", exp_path) ||
|
||||
exp_path == L"..")
|
||||
{
|
||||
/* These paths can be implicit cd. Note that a single period cannot (that's used for sourcing files anyways) */
|
||||
wchar_t *cd_path = path_allocate_cdpath(exp_path, wd);
|
||||
if (cd_path)
|
||||
{
|
||||
/* It worked. Return the path if desired */
|
||||
if (out_path)
|
||||
out_path->assign(cd_path);
|
||||
free(cd_path);
|
||||
result = true;
|
||||
}
|
||||
/* These paths can be implicit cd, so see if you cd to the path. Note that a single period cannot (that's used for sourcing files anyways) */
|
||||
result = path_get_cdpath(exp_path, out_path, wd, vars);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -548,10 +399,10 @@ bool path_is_valid(const wcstring &path, const wcstring &working_directory)
|
||||
/* Prepend the working directory. Note that we know path is not empty here. */
|
||||
wcstring tmp = working_directory;
|
||||
tmp.append(path);
|
||||
path_is_valid = (0 == waccess(tmp.c_str(), F_OK));
|
||||
path_is_valid = (0 == waccess(tmp, F_OK));
|
||||
} else {
|
||||
/* Simple check */
|
||||
path_is_valid = (0 == waccess(path.c_str(), F_OK));
|
||||
path_is_valid = (0 == waccess(path, F_OK));
|
||||
}
|
||||
return path_is_valid;
|
||||
}
|
||||
|
||||
33
path.h
33
path.h
@@ -9,6 +9,8 @@
|
||||
#ifndef FISH_PATH_H
|
||||
#define FISH_PATH_H
|
||||
|
||||
#include "env.h"
|
||||
|
||||
/**
|
||||
Return value for path_cdpath_get when locatied a rotten symlink
|
||||
*/
|
||||
@@ -24,19 +26,16 @@
|
||||
bool path_get_config(wcstring &path);
|
||||
|
||||
/**
|
||||
Finds the full path of an executable in a newly allocated string.
|
||||
Finds the full path of an executable. Returns YES if successful.
|
||||
|
||||
\param cmd The name of the executable.
|
||||
\param output_or_NULL If non-NULL, store the full path.
|
||||
\param vars The environment variables snapshot to use
|
||||
\return 0 if the command can not be found, the path of the command otherwise. The result should be freed with free().
|
||||
*/
|
||||
wchar_t *path_get_path( const wchar_t *cmd );
|
||||
|
||||
/** Returns whether the path can be used for an implicit cd command; if so, also returns the path by reference (if desired). This requires it to start with one of the allowed prefixes (., .., ~) and resolve to a directory. */
|
||||
bool path_can_be_implicit_cd(const wcstring &path, wcstring *out_path = NULL, const wchar_t *wd = NULL);
|
||||
|
||||
class env_vars;
|
||||
bool path_get_path_string(const wcstring &cmd, wcstring &output);
|
||||
bool path_get_path_string(const wcstring &cmd, wcstring &output, const env_vars &vars);
|
||||
bool path_get_path(const wcstring &cmd,
|
||||
wcstring *output_or_NULL,
|
||||
const env_vars_snapshot_t &vars = env_vars_snapshot_t::current());
|
||||
|
||||
/**
|
||||
Returns the full path of the specified directory, using the CDPATH
|
||||
@@ -51,14 +50,22 @@ bool path_get_path_string(const wcstring &cmd, wcstring &output, const env_vars
|
||||
symlink and a file are found, it is undefined which error status
|
||||
will be returned.
|
||||
|
||||
\param in The name of the directory.
|
||||
\param dir The name of the directory.
|
||||
\param out_or_NULL If non-NULL, return the path to the resolved directory
|
||||
\param wd The working directory, or NULL to use the default. The working directory should have a slash appended at the end.
|
||||
\param vars The environment variable snapshot to use (for the CDPATH variable)
|
||||
\return 0 if the command can not be found, the path of the command otherwise. The path should be free'd with free().
|
||||
*/
|
||||
bool path_get_cdpath(const wcstring &dir,
|
||||
wcstring *out_or_NULL,
|
||||
const wchar_t *wd = NULL,
|
||||
const env_vars_snapshot_t &vars = env_vars_snapshot_t::current());
|
||||
|
||||
wchar_t *path_allocate_cdpath( const wcstring &in, const wchar_t *wd = NULL);
|
||||
bool path_can_get_cdpath(const wcstring &in, const wchar_t *wd = NULL);
|
||||
bool path_get_cdpath_string(const wcstring &in, wcstring &out, const env_vars &vars);
|
||||
/** Returns whether the path can be used for an implicit cd command; if so, also returns the path by reference (if desired). This requires it to start with one of the allowed prefixes (., .., ~) and resolve to a directory. */
|
||||
bool path_can_be_implicit_cd(const wcstring &path,
|
||||
wcstring *out_path = NULL,
|
||||
const wchar_t *wd = NULL,
|
||||
const env_vars_snapshot_t &vars = env_vars_snapshot_t::current());
|
||||
|
||||
/**
|
||||
Remove double slashes and trailing slashes from a path,
|
||||
|
||||
347
postfork.cpp
347
postfork.cpp
@@ -9,7 +9,6 @@
|
||||
#include "iothread.h"
|
||||
#include "exec.h"
|
||||
|
||||
|
||||
/** The number of times to try to call fork() before giving up */
|
||||
#define FORK_LAPS 5
|
||||
|
||||
@@ -102,39 +101,49 @@ int set_child_group( job_t *j, process_t *p, int print_errors )
|
||||
return res;
|
||||
}
|
||||
|
||||
/** Make sure the fd used by this redirection is not used by i.e. a pipe. */
|
||||
static void free_fd( io_data_t *io, int fd )
|
||||
/** Make sure the fd used by each redirection is not used by a pipe. */
|
||||
static void free_redirected_fds_from_pipes(io_chain_t &io_chain)
|
||||
{
|
||||
if( !io )
|
||||
return;
|
||||
|
||||
if( ( io->io_mode == IO_PIPE ) || ( io->io_mode == IO_BUFFER ) )
|
||||
{
|
||||
int i;
|
||||
for( i=0; i<2; i++ )
|
||||
{
|
||||
if(io->param1.pipe_fd[i] == fd )
|
||||
{
|
||||
while(1)
|
||||
{
|
||||
if( (io->param1.pipe_fd[i] = dup(fd)) == -1)
|
||||
{
|
||||
if( errno != EINTR )
|
||||
{
|
||||
debug_safe_int( 1, FD_ERROR, fd );
|
||||
wperror( L"dup" );
|
||||
FATAL_EXIT();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
size_t max = io_chain.size();
|
||||
for (size_t i = 0; i < max; i++)
|
||||
{
|
||||
int fd_to_free = io_chain.at(i)->fd;
|
||||
|
||||
/* We only have to worry about fds beyond the three standard ones */
|
||||
if (fd_to_free <= 2)
|
||||
continue;
|
||||
|
||||
/* Make sure the fd is not used by a pipe */
|
||||
for (size_t j = 0; j < max; j++)
|
||||
{
|
||||
/* We're only interested in pipes */
|
||||
io_data_t *possible_conflict = io_chain.at(j);
|
||||
if (possible_conflict->io_mode != IO_PIPE && possible_conflict->io_mode != IO_BUFFER)
|
||||
continue;
|
||||
|
||||
/* If the pipe is a conflict, dup it to some other value */
|
||||
for (int k=0; k<2; k++)
|
||||
{
|
||||
/* If it's not a conflict, we don't care */
|
||||
if (possible_conflict->param1.pipe_fd[k] != fd_to_free)
|
||||
continue;
|
||||
|
||||
/* Repeat until we have a replacement fd */
|
||||
int replacement_fd = -1;
|
||||
while (replacement_fd < 0)
|
||||
{
|
||||
replacement_fd = dup(fd_to_free);
|
||||
if (replacement_fd == -1 && errno != EINTR)
|
||||
{
|
||||
debug_safe_int( 1, FD_ERROR, fd_to_free );
|
||||
wperror( L"dup" );
|
||||
FATAL_EXIT();
|
||||
}
|
||||
}
|
||||
possible_conflict->param1.pipe_fd[k] = replacement_fd;
|
||||
}
|
||||
}
|
||||
}
|
||||
free_fd( io->next, fd );
|
||||
}
|
||||
|
||||
|
||||
@@ -150,25 +159,20 @@ static void free_fd( io_data_t *io, int fd )
|
||||
|
||||
\return 0 on sucess, -1 on failiure
|
||||
*/
|
||||
static int handle_child_io( io_data_t *io )
|
||||
static int handle_child_io( io_chain_t &io_chain )
|
||||
{
|
||||
|
||||
close_unused_internal_pipes( io );
|
||||
|
||||
for( ; io; io=io->next )
|
||||
close_unused_internal_pipes( io_chain );
|
||||
free_redirected_fds_from_pipes(io_chain);
|
||||
for (size_t idx = 0; idx < io_chain.size(); idx++)
|
||||
{
|
||||
io_data_t *io = io_chain.at(idx);
|
||||
int tmp;
|
||||
|
||||
|
||||
if( io->io_mode == IO_FD && io->fd == io->param1.old_fd )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if( io->fd > 2 )
|
||||
{
|
||||
/* Make sure the fd used by this redirection is not used by e.g. a pipe. */
|
||||
free_fd( io, io->fd );
|
||||
}
|
||||
|
||||
switch( io->io_mode )
|
||||
{
|
||||
@@ -184,7 +188,7 @@ static int handle_child_io( io_data_t *io )
|
||||
|
||||
case IO_FILE:
|
||||
{
|
||||
// Here we definitely do not want to set CLO_EXEC because our child needs access
|
||||
// Here we definitely do not want to set CLO_EXEC because our child needs access
|
||||
if( (tmp=open( io->filename_cstr,
|
||||
io->param2.flags, OPEN_MASK ) )==-1 )
|
||||
{
|
||||
@@ -240,6 +244,7 @@ static int handle_child_io( io_data_t *io )
|
||||
case IO_BUFFER:
|
||||
case IO_PIPE:
|
||||
{
|
||||
/* If write_pipe_idx is 0, it means we're connecting to the read end (first pipe fd). If it's 1, we're connecting to the write end (second pipe fd). */
|
||||
unsigned int write_pipe_idx = (io->is_input ? 0 : 1);
|
||||
/*
|
||||
debug( 0,
|
||||
@@ -256,16 +261,11 @@ static int handle_child_io( io_data_t *io )
|
||||
perror( "dup2" );
|
||||
return -1;
|
||||
}
|
||||
|
||||
if( write_pipe_idx > 0 )
|
||||
{
|
||||
exec_close( io->param1.pipe_fd[0]);
|
||||
exec_close( io->param1.pipe_fd[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
exec_close( io->param1.pipe_fd[0] );
|
||||
}
|
||||
|
||||
if (io->param1.pipe_fd[0] >= 0)
|
||||
exec_close( io->param1.pipe_fd[0]);
|
||||
if (io->param1.pipe_fd[1] >= 0)
|
||||
exec_close( io->param1.pipe_fd[1]);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -304,8 +304,7 @@ int setup_child_process( job_t *j, process_t *p )
|
||||
/* Remove all signal blocks */
|
||||
signal_unblock();
|
||||
|
||||
return ok ? 0 : -1;
|
||||
|
||||
return ok ? 0 : -1;
|
||||
}
|
||||
|
||||
int g_fork_count = 0;
|
||||
@@ -362,3 +361,239 @@ pid_t execute_fork(bool wait_for_threads_to_die)
|
||||
FATAL_EXIT();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if FISH_USE_POSIX_SPAWN
|
||||
bool fork_actions_make_spawn_properties(posix_spawnattr_t *attr, posix_spawn_file_actions_t *actions, job_t *j, process_t *p)
|
||||
{
|
||||
/* Initialize the output */
|
||||
if (posix_spawnattr_init(attr) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (posix_spawn_file_actions_init(actions) != 0) {
|
||||
posix_spawnattr_destroy(attr);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool should_set_parent_group_id = false;
|
||||
int desired_parent_group_id = 0;
|
||||
if (job_get_flag(j, JOB_CONTROL))
|
||||
{
|
||||
should_set_parent_group_id = true;
|
||||
|
||||
// PCA: I'm quite fuzzy on process groups,
|
||||
// but I believe that the default value of 0
|
||||
// means that the process becomes its own
|
||||
// group leader, which is what set_child_group did
|
||||
// in this case. So we want this to be 0 if j->pgid is 0.
|
||||
desired_parent_group_id = j->pgid;
|
||||
}
|
||||
|
||||
/* Set the handling for job control signals back to the default. */
|
||||
bool reset_signal_handlers = true;
|
||||
|
||||
/* Remove all signal blocks */
|
||||
bool reset_sigmask = true;
|
||||
|
||||
/* Set our flags */
|
||||
short flags = 0;
|
||||
if (reset_signal_handlers)
|
||||
flags |= POSIX_SPAWN_SETSIGDEF;
|
||||
if (reset_sigmask)
|
||||
flags |= POSIX_SPAWN_SETSIGMASK;
|
||||
if (should_set_parent_group_id)
|
||||
flags |= POSIX_SPAWN_SETPGROUP;
|
||||
|
||||
int err = 0;
|
||||
if (! err)
|
||||
err = posix_spawnattr_setflags(attr, flags);
|
||||
|
||||
if (! err && should_set_parent_group_id)
|
||||
err = posix_spawnattr_setpgroup(attr, desired_parent_group_id);
|
||||
|
||||
/* Everybody gets default handlers */
|
||||
if (! err && reset_signal_handlers)
|
||||
{
|
||||
sigset_t sigdefault;
|
||||
get_signals_with_handlers(&sigdefault);
|
||||
err = posix_spawnattr_setsigdefault(attr, &sigdefault);
|
||||
}
|
||||
|
||||
/* No signals blocked */
|
||||
sigset_t sigmask;
|
||||
sigemptyset(&sigmask);
|
||||
if (! err && reset_sigmask)
|
||||
err = posix_spawnattr_setsigmask(attr, &sigmask);
|
||||
|
||||
/* Make sure that our pipes don't use an fd that the redirection itself wants to use */
|
||||
free_redirected_fds_from_pipes(j->io);
|
||||
|
||||
/* Close unused internal pipes */
|
||||
std::vector<int> files_to_close;
|
||||
get_unused_internal_pipes(files_to_close, j->io);
|
||||
for (size_t i = 0; ! err && i < files_to_close.size(); i++)
|
||||
{
|
||||
err = posix_spawn_file_actions_addclose(actions, files_to_close.at(i));
|
||||
}
|
||||
|
||||
for (size_t idx = 0; idx < j->io.size(); idx++)
|
||||
{
|
||||
const io_data_t *io = j->io.at(idx);
|
||||
|
||||
if( io->io_mode == IO_FD && io->fd == io->param1.old_fd )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if( io->fd > 2 )
|
||||
{
|
||||
/* Make sure the fd used by this redirection is not used by e.g. a pipe. */
|
||||
// free_fd(io_chain, io->fd );
|
||||
// PCA I don't think we need to worry about this. fd redirection is pretty uncommon anyways.
|
||||
}
|
||||
switch (io->io_mode)
|
||||
{
|
||||
case IO_CLOSE:
|
||||
{
|
||||
if (! err)
|
||||
err = posix_spawn_file_actions_addclose(actions, io->fd);
|
||||
break;
|
||||
}
|
||||
|
||||
case IO_FILE:
|
||||
{
|
||||
if (! err)
|
||||
err = posix_spawn_file_actions_addopen(actions, io->fd, io->filename_cstr, io->param2.flags /* mode */, OPEN_MASK);
|
||||
break;
|
||||
}
|
||||
|
||||
case IO_FD:
|
||||
{
|
||||
if (! err)
|
||||
err = posix_spawn_file_actions_adddup2(actions, io->param1.old_fd /* from */, io->fd /* to */);
|
||||
break;
|
||||
}
|
||||
|
||||
case IO_BUFFER:
|
||||
case IO_PIPE:
|
||||
{
|
||||
unsigned int write_pipe_idx = (io->is_input ? 0 : 1);
|
||||
int from_fd = io->param1.pipe_fd[write_pipe_idx];
|
||||
int to_fd = io->fd;
|
||||
if (! err)
|
||||
err = posix_spawn_file_actions_adddup2(actions, from_fd, to_fd);
|
||||
|
||||
|
||||
if( write_pipe_idx > 0 )
|
||||
{
|
||||
if (! err)
|
||||
err = posix_spawn_file_actions_addclose(actions, io->param1.pipe_fd[0]);
|
||||
if (! err)
|
||||
err = posix_spawn_file_actions_addclose(actions, io->param1.pipe_fd[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (! err)
|
||||
err = posix_spawn_file_actions_addclose(actions, io->param1.pipe_fd[0]);
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Clean up on error */
|
||||
if (err) {
|
||||
posix_spawnattr_destroy(attr);
|
||||
posix_spawn_file_actions_destroy(actions);
|
||||
}
|
||||
|
||||
return ! err;
|
||||
}
|
||||
#endif //FISH_USE_POSIX_SPAWN
|
||||
|
||||
void safe_report_exec_error(int err, const char *actual_cmd, char **argv, char **envv)
|
||||
{
|
||||
debug_safe( 0, "Failed to execute process '%s'. Reason:", actual_cmd );
|
||||
|
||||
switch( err )
|
||||
{
|
||||
|
||||
case E2BIG:
|
||||
{
|
||||
char sz1[128], sz2[128];
|
||||
|
||||
long arg_max = -1;
|
||||
|
||||
size_t sz = 0;
|
||||
char **p;
|
||||
for(p=argv; *p; p++)
|
||||
{
|
||||
sz += strlen(*p)+1;
|
||||
}
|
||||
|
||||
for(p=envv; *p; p++)
|
||||
{
|
||||
sz += strlen(*p)+1;
|
||||
}
|
||||
|
||||
format_size_safe(sz1, sz);
|
||||
arg_max = sysconf( _SC_ARG_MAX );
|
||||
|
||||
if( arg_max > 0 )
|
||||
{
|
||||
format_size_safe(sz2, sz);
|
||||
debug_safe(0, "The total size of the argument and environment lists %s exceeds the operating system limit of %s.", sz1, sz2);
|
||||
}
|
||||
else
|
||||
{
|
||||
debug_safe( 0, "The total size of the argument and environment lists (%s) exceeds the operating system limit.", sz1);
|
||||
}
|
||||
|
||||
debug_safe(0, "Try running the command again with fewer arguments.");
|
||||
break;
|
||||
}
|
||||
|
||||
case ENOEXEC:
|
||||
{
|
||||
/* Hope strerror doesn't allocate... */
|
||||
const char *err = strerror(errno);
|
||||
debug_safe(0, "exec: %s", err);
|
||||
|
||||
debug_safe(0, "The file '%s' is marked as an executable but could not be run by the operating system.", actual_cmd);
|
||||
break;
|
||||
}
|
||||
|
||||
case ENOENT:
|
||||
{
|
||||
/* ENOENT is returned by exec() when the path fails, but also returned by posix_spawn if an open file action fails. These cases appear to be impossible to distinguish. We address this by not using posix_spawn for file redirections, so all the ENOENTs we find must be errors from exec(). */
|
||||
char interpreter_buff[128] = {}, *interpreter;
|
||||
interpreter = get_interpreter(actual_cmd, interpreter_buff, sizeof interpreter_buff);
|
||||
if( interpreter && 0 != access( interpreter, X_OK ) )
|
||||
{
|
||||
debug_safe(0, "The file '%s' specified the interpreter '%s', which is not an executable command.", actual_cmd, interpreter );
|
||||
}
|
||||
else
|
||||
{
|
||||
debug_safe(0, "The file '%s' does not exist or could not be executed.", actual_cmd);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ENOMEM:
|
||||
{
|
||||
debug_safe(0, "Out of memory");
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
/* Hope strerror doesn't allocate... */
|
||||
const char *err = strerror(errno);
|
||||
debug_safe(0, "exec: %s", err);
|
||||
|
||||
// debug(0, L"The file '%ls' is marked as an executable but could not be run by the operating system.", p->actual_cmd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
18
postfork.h
18
postfork.h
@@ -19,6 +19,15 @@
|
||||
#include "wutil.h"
|
||||
#include "io.h"
|
||||
|
||||
#if HAVE_SPAWN_H
|
||||
#include <spawn.h>
|
||||
#endif
|
||||
|
||||
#ifndef FISH_USE_POSIX_SPAWN
|
||||
#define FISH_USE_POSIX_SPAWN HAVE_SPAWN_H
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
This function should be called by both the parent process and the
|
||||
child right after fork() has been called. If job control is
|
||||
@@ -44,6 +53,7 @@ int set_child_group( job_t *j, process_t *p, int print_errors );
|
||||
|
||||
\param j the job to set up the IO for
|
||||
\param p the child process to set up
|
||||
\param io_chain the IO chain to use (ignores the job's iochain)
|
||||
|
||||
\return 0 on sucess, -1 on failiure. When this function returns,
|
||||
signals are always unblocked. On failiure, signal handlers, io
|
||||
@@ -55,4 +65,12 @@ int setup_child_process( job_t *j, process_t *p );
|
||||
*/
|
||||
pid_t execute_fork(bool wait_for_threads_to_die);
|
||||
|
||||
/** Report an error from failing to exec or posix_spawn a command */
|
||||
void safe_report_exec_error(int err, const char *actual_cmd, char **argv, char **envv);
|
||||
|
||||
#if FISH_USE_POSIX_SPAWN
|
||||
/* Initializes and fills in a posix_spawnattr_t; on success, the caller should destroy it via posix_spawnattr_destroy */
|
||||
bool fork_actions_make_spawn_properties(posix_spawnattr_t *attr, posix_spawn_file_actions_t *actions, job_t *j, process_t *p);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
100
proc.cpp
100
proc.cpp
@@ -72,7 +72,6 @@ Some of the code in this file is based on code from the Glibc manual.
|
||||
#include "signal.h"
|
||||
#include "event.h"
|
||||
|
||||
#include <deque>
|
||||
#include "output.h"
|
||||
|
||||
/**
|
||||
@@ -153,7 +152,7 @@ static event_t event(0);
|
||||
/**
|
||||
A stack containing the values of is_interactive. Used by proc_push_interactive and proc_pop_interactive.
|
||||
*/
|
||||
static std::deque<int> interactive_stack;
|
||||
static std::vector<int> interactive_stack;
|
||||
|
||||
void proc_init()
|
||||
{
|
||||
@@ -224,7 +223,7 @@ job_id_t acquire_job_id(void)
|
||||
{
|
||||
/* We found a slot. Note that slot 0 corresponds to job ID 1. */
|
||||
*slot = true;
|
||||
return slot - consumed_job_ids.begin() + 1;
|
||||
return (job_id_t)(slot - consumed_job_ids.begin() + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -307,7 +306,7 @@ void job_set_flag( job_t *j, int flag, int set )
|
||||
if( set )
|
||||
j->flags |= flag;
|
||||
else
|
||||
j->flags = j->flags & (0xffffffff ^ flag);
|
||||
j->flags = j->flags & ((unsigned int)(-1) ^ flag);
|
||||
}
|
||||
|
||||
int job_get_flag( const job_t *j, int flag )
|
||||
@@ -351,7 +350,8 @@ int job_signal( job_t *j, int signal )
|
||||
|
||||
/**
|
||||
Store the status of the process pid that was returned by waitpid.
|
||||
Return 0 if all went well, nonzero otherwise.
|
||||
Return 0 if all went well, nonzero otherwise.
|
||||
This is called from a signal handler.
|
||||
*/
|
||||
static void mark_process_status( const job_t *j,
|
||||
process_t *p,
|
||||
@@ -378,8 +378,8 @@ static void mark_process_status( const job_t *j,
|
||||
char mess[MESS_SIZE];
|
||||
snprintf( mess,
|
||||
MESS_SIZE,
|
||||
"Process %d exited abnormally\n",
|
||||
(int) p->pid );
|
||||
"Process %ld exited abnormally\n",
|
||||
(long) p->pid );
|
||||
/*
|
||||
If write fails, do nothing. We're in a signal handlers error
|
||||
handler. If things aren't working properly, it's safer to
|
||||
@@ -389,6 +389,12 @@ static void mark_process_status( const job_t *j,
|
||||
}
|
||||
}
|
||||
|
||||
void job_mark_process_as_failed( const job_t *job, process_t *p )
|
||||
{
|
||||
/* The given process failed to even lift off (e.g. posix_spawn failed) and so doesn't have a valid pid. Mark it as dead. */
|
||||
p->completed = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
Handle status update for child \c pid. This function is called by
|
||||
the signal handler, so it mustn't use malloc or any such hitech
|
||||
@@ -492,6 +498,51 @@ static void handle_child_status( pid_t pid, int status )
|
||||
return;
|
||||
}
|
||||
|
||||
process_t::process_t() :
|
||||
argv_array(),
|
||||
argv0_narrow(),
|
||||
type(0),
|
||||
actual_cmd(),
|
||||
pid(0),
|
||||
pipe_write_fd(0),
|
||||
pipe_read_fd(0),
|
||||
completed(0),
|
||||
stopped(0),
|
||||
status(0),
|
||||
count_help_magic(0),
|
||||
next(NULL)
|
||||
#ifdef HAVE__PROC_SELF_STAT
|
||||
,last_time(),
|
||||
last_jiffies(0)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
process_t::~process_t()
|
||||
{
|
||||
if (this->next != NULL)
|
||||
delete this->next;
|
||||
}
|
||||
|
||||
job_t::job_t(job_id_t jobid) :
|
||||
command_str(),
|
||||
command_narrow(),
|
||||
first_process(NULL),
|
||||
pgid(0),
|
||||
tmodes(),
|
||||
job_id(jobid),
|
||||
io(),
|
||||
flags(0)
|
||||
{
|
||||
}
|
||||
|
||||
job_t::~job_t()
|
||||
{
|
||||
if (first_process != NULL)
|
||||
delete first_process;
|
||||
io_chain_destroy(this->io);
|
||||
release_job_id(job_id);
|
||||
}
|
||||
|
||||
/* This is called from a signal handler */
|
||||
void job_handle_signal ( int signal, siginfo_t *info, void *con )
|
||||
@@ -809,12 +860,12 @@ static int select_try( job_t *j )
|
||||
{
|
||||
fd_set fds;
|
||||
int maxfd=-1;
|
||||
io_data_t *d;
|
||||
|
||||
FD_ZERO(&fds);
|
||||
|
||||
for( d = j->io; d; d=d->next )
|
||||
for (size_t idx = 0; idx < j->io.size(); idx++)
|
||||
{
|
||||
const io_data_t *d = j->io.at(idx);
|
||||
if( d->io_mode == IO_BUFFER )
|
||||
{
|
||||
int fd = d->param1.pipe_fd[0];
|
||||
@@ -847,14 +898,14 @@ static int select_try( job_t *j )
|
||||
*/
|
||||
static void read_try( job_t *j )
|
||||
{
|
||||
io_data_t *d, *buff=0;
|
||||
io_data_t *buff=NULL;
|
||||
|
||||
/*
|
||||
Find the last buffer, which is the one we want to read from
|
||||
*/
|
||||
for( d = j->io; d; d=d->next )
|
||||
for (size_t idx = 0; idx < j->io.size(); idx++)
|
||||
{
|
||||
|
||||
io_data_t *d = j->io.at(idx);
|
||||
if( d->io_mode == IO_BUFFER )
|
||||
{
|
||||
buff=d;
|
||||
@@ -867,7 +918,7 @@ static void read_try( job_t *j )
|
||||
while(1)
|
||||
{
|
||||
char b[BUFFER_SIZE];
|
||||
int l;
|
||||
long l;
|
||||
|
||||
l=read_blocked( buff->param1.pipe_fd[0],
|
||||
b, BUFFER_SIZE );
|
||||
@@ -956,6 +1007,11 @@ static int terminal_return_from_job( job_t *j)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Disabling this per https://github.com/adityagodbole/fish-shell/commit/9d229cd18c3e5c25a8bd37e9ddd3b67ddc2d1b72
|
||||
On Linux, 'cd . ; ftp' prevents you from typing into the ftp prompt
|
||||
See https://github.com/fish-shell/fish-shell/issues/121
|
||||
*/
|
||||
#if 0
|
||||
/*
|
||||
Restore the shell's terminal modes.
|
||||
*/
|
||||
@@ -965,6 +1021,7 @@ static int terminal_return_from_job( job_t *j)
|
||||
wperror( L"tcsetattr" );
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -1054,14 +1111,6 @@ void job_continue (job_t *j, int cont)
|
||||
}
|
||||
while (got_signal && !quit);
|
||||
|
||||
if (quit) {
|
||||
// It's possible that the job will produce output and exit before we've even read from it.
|
||||
// We'll eventually read the output, but it may be after we've executed subsequent calls
|
||||
// This is why my prompt colors kept getting screwed up - the builtin echo calls
|
||||
// were sometimes having their output combined with the set_color calls in the wrong order!
|
||||
read_try(j);
|
||||
}
|
||||
|
||||
if( !quit )
|
||||
{
|
||||
|
||||
@@ -1119,6 +1168,14 @@ void job_continue (job_t *j, int cont)
|
||||
|
||||
if( job_is_completed( j ))
|
||||
{
|
||||
|
||||
// It's possible that the job will produce output and exit before we've even read from it.
|
||||
// We'll eventually read the output, but it may be after we've executed subsequent calls
|
||||
// This is why my prompt colors kept getting screwed up - the builtin echo calls
|
||||
// were sometimes having their output combined with the set_color calls in the wrong order!
|
||||
read_try(j);
|
||||
|
||||
|
||||
process_t *p = j->first_process;
|
||||
while( p->next )
|
||||
p = p->next;
|
||||
@@ -1213,7 +1270,6 @@ void proc_sanity_check()
|
||||
validate_pointer( p->get_argv(), _( L"Process argument list" ), 0 );
|
||||
validate_pointer( p->argv0(), _( L"Process name" ), 0 );
|
||||
validate_pointer( p->next, _( L"Process list pointer" ), 1 );
|
||||
validate_pointer( p->actual_cmd, _( L"Process command" ), 1 );
|
||||
|
||||
if ( (p->stopped & (~0x00000001)) != 0 )
|
||||
{
|
||||
|
||||
170
proc.h
170
proc.h
@@ -134,38 +134,15 @@ class process_t
|
||||
/* narrow copy of argv0 so we don't have to convert after fork */
|
||||
narrow_string_rep_t argv0_narrow;
|
||||
|
||||
|
||||
/* No copying */
|
||||
process_t(const process_t &rhs) { }
|
||||
void operator=(const process_t &rhs) { }
|
||||
process_t(const process_t &rhs);
|
||||
void operator=(const process_t &rhs);
|
||||
|
||||
public:
|
||||
|
||||
process_t() :
|
||||
argv_array(),
|
||||
type(0),
|
||||
actual_cmd(NULL),
|
||||
pid(0),
|
||||
pipe_write_fd(0),
|
||||
pipe_read_fd(0),
|
||||
completed(0),
|
||||
stopped(0),
|
||||
status(0),
|
||||
count_help_magic(0),
|
||||
next(NULL)
|
||||
#ifdef HAVE__PROC_SELF_STAT
|
||||
,last_time(),
|
||||
last_jiffies(0)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
process_t();
|
||||
|
||||
~process_t()
|
||||
{
|
||||
if (this->next != NULL)
|
||||
delete this->next;
|
||||
free((void *)actual_cmd); //may be NULL
|
||||
}
|
||||
~process_t();
|
||||
|
||||
/**
|
||||
Type of process. Can be one of \c EXTERNAL, \c
|
||||
@@ -203,8 +180,8 @@ class process_t
|
||||
return argv0_narrow.get();
|
||||
}
|
||||
|
||||
/** actual command to pass to exec in case of EXTERNAL or INTERNAL_EXEC. malloc'd! */
|
||||
const wchar_t *actual_cmd;
|
||||
/** actual command to pass to exec in case of EXTERNAL or INTERNAL_EXEC. */
|
||||
wcstring actual_cmd;
|
||||
|
||||
/** process ID */
|
||||
pid_t pid;
|
||||
@@ -237,64 +214,42 @@ class process_t
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
Constant for the flag variable in the job struct
|
||||
|
||||
true if user was told about stopped job
|
||||
*/
|
||||
#define JOB_NOTIFIED 1
|
||||
/**
|
||||
Constant for the flag variable in the job struct
|
||||
|
||||
Whether this job is in the foreground
|
||||
*/
|
||||
#define JOB_FOREGROUND 2
|
||||
/**
|
||||
Constant for the flag variable in the job struct
|
||||
|
||||
/* Constants for the flag variable in the job struct */
|
||||
enum {
|
||||
/** true if user was told about stopped job */
|
||||
JOB_NOTIFIED = 1 << 0,
|
||||
|
||||
/** Whether this job is in the foreground */
|
||||
JOB_FOREGROUND = 1 << 1,
|
||||
|
||||
/**
|
||||
Whether the specified job is completely constructed,
|
||||
i.e. completely parsed, and every process in the job has been
|
||||
forked, etc.
|
||||
*/
|
||||
#define JOB_CONSTRUCTED 4
|
||||
/**
|
||||
Constant for the flag variable in the job struct
|
||||
*/
|
||||
JOB_CONSTRUCTED = 1 << 2,
|
||||
|
||||
/** Whether the specified job is a part of a subshell, event handler or some other form of special job that should not be reported */
|
||||
JOB_SKIP_NOTIFICATION = 1 << 3,
|
||||
|
||||
Whether the specified job is a part of a subshell, event handler or some other form of special job that should not be reported
|
||||
*/
|
||||
#define JOB_SKIP_NOTIFICATION 8
|
||||
/**
|
||||
Constant for the flag variable in the job struct
|
||||
/** Should the exit status be negated? This flag can only be set by the not builtin. */
|
||||
JOB_NEGATE = 1 << 4,
|
||||
|
||||
/** Should the exit status be used to reevaluate the condition in an if block? This is only used by elseif and is a big hack. */
|
||||
JOB_ELSEIF = 1 << 5,
|
||||
|
||||
/** This flag is set to one on wildcard expansion errors. It means that the current command should not be executed */
|
||||
JOB_WILDCARD_ERROR = 1 << 6,
|
||||
|
||||
/** Skip executing this job. This flag is set by the short-circut builtins, i.e. and and or */
|
||||
JOB_SKIP = 1 << 7,
|
||||
|
||||
/** Whether the job is under job control */
|
||||
JOB_CONTROL = 1 << 8,
|
||||
|
||||
Should the exit status be negated? This flag can only be set by the not builtin.
|
||||
*/
|
||||
#define JOB_NEGATE 16
|
||||
/**
|
||||
Constant for the flag variable in the job struct
|
||||
|
||||
This flag is set to one on wildcard expansion errors. It means that the current command should not be executed
|
||||
*/
|
||||
#define JOB_WILDCARD_ERROR 32
|
||||
|
||||
/**
|
||||
Constant for the flag variable in the job struct
|
||||
|
||||
Skip executing this job. This flag is set by the short-circut builtins, i.e. and and or
|
||||
*/
|
||||
#define JOB_SKIP 64
|
||||
|
||||
/**
|
||||
Constant for the flag variable in the job struct
|
||||
|
||||
Whether the job is under job control
|
||||
*/
|
||||
#define JOB_CONTROL 128
|
||||
/**
|
||||
Constant for the flag variable in the job struct
|
||||
|
||||
Whether the job wants to own the terminal when in the foreground
|
||||
*/
|
||||
#define JOB_TERMINAL 256
|
||||
/** Whether the job wants to own the terminal when in the foreground */
|
||||
JOB_TERMINAL = 1 << 9
|
||||
};
|
||||
|
||||
/**
|
||||
A struct represeting a job. A job is basically a pipeline of one
|
||||
@@ -311,52 +266,35 @@ class job_t
|
||||
job. It is used for displaying messages about job status
|
||||
on the terminal.
|
||||
*/
|
||||
wcstring command;
|
||||
wcstring command_str;
|
||||
|
||||
/* narrow copy so we don't have to convert after fork */
|
||||
narrow_string_rep_t command_narrow;
|
||||
|
||||
/* No copying */
|
||||
job_t(const job_t &rhs) : job_id(0) { }
|
||||
void operator=(const job_t &) { }
|
||||
job_t(const job_t &rhs);
|
||||
void operator=(const job_t &);
|
||||
|
||||
public:
|
||||
|
||||
job_t(job_id_t jobid) :
|
||||
command(),
|
||||
first_process(NULL),
|
||||
pgid(0),
|
||||
tmodes(),
|
||||
job_id(jobid),
|
||||
io(NULL),
|
||||
flags(0)
|
||||
{
|
||||
}
|
||||
|
||||
~job_t() {
|
||||
if (first_process != NULL)
|
||||
delete first_process;
|
||||
io_data_t *data = this->io;
|
||||
while (data) {
|
||||
io_data_t *tmp = data->next;
|
||||
delete data;
|
||||
data = tmp;
|
||||
}
|
||||
release_job_id(job_id);
|
||||
}
|
||||
job_t(job_id_t jobid);
|
||||
~job_t();
|
||||
|
||||
/** Returns whether the command is empty. */
|
||||
bool command_is_empty() const { return command.empty(); }
|
||||
bool command_is_empty() const { return command_str.empty(); }
|
||||
|
||||
/** Returns the command as a wchar_t *. */
|
||||
const wchar_t *command_wcstr() const { return command.c_str(); }
|
||||
const wchar_t *command_wcstr() const { return command_str.c_str(); }
|
||||
|
||||
/** Returns the command */
|
||||
const wcstring &command() const { return command_str; }
|
||||
|
||||
/** Returns the command as a char *. */
|
||||
const char *command_cstr() const { return command_narrow.get(); }
|
||||
|
||||
/** Sets the command */
|
||||
void set_command(const wcstring &cmd) {
|
||||
command = cmd;
|
||||
command_str = cmd;
|
||||
command_narrow.set(cmd);
|
||||
}
|
||||
|
||||
@@ -386,16 +324,13 @@ class job_t
|
||||
*/
|
||||
const job_id_t job_id;
|
||||
|
||||
/**
|
||||
List of all IO redirections for this job. This linked list is allocated via new, and owned by the object, which should delete them.
|
||||
*/
|
||||
io_data_t *io;
|
||||
/** List of all IO redirections for this job. */
|
||||
io_chain_t io;
|
||||
|
||||
/**
|
||||
Bitset containing information about the job. A combination of the JOB_* constants.
|
||||
*/
|
||||
int flags;
|
||||
|
||||
unsigned int flags;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -574,6 +509,9 @@ void job_handle_signal( int signal, siginfo_t *info, void *con );
|
||||
*/
|
||||
int job_signal( job_t *j, int signal );
|
||||
|
||||
/* Marks a process as failed to execute (and therefore completed) */
|
||||
void job_mark_process_as_failed( const job_t *job, process_t *p );
|
||||
|
||||
#ifdef HAVE__PROC_SELF_STAT
|
||||
/**
|
||||
Use the procfs filesystem to look up how many jiffies of cpu time
|
||||
|
||||
1440
reader.cpp
1440
reader.cpp
File diff suppressed because it is too large
Load Diff
27
reader.h
27
reader.h
@@ -24,7 +24,7 @@ class history_t;
|
||||
/**
|
||||
Read commands from \c fd until encountering EOF
|
||||
*/
|
||||
int reader_read( int fd, io_data_t *io);
|
||||
int reader_read( int fd, const io_chain_t &io);
|
||||
|
||||
/**
|
||||
Tell the shell that it should exit after the currently running command finishes.
|
||||
@@ -100,15 +100,16 @@ history_t *reader_get_history(void);
|
||||
Set the string of characters in the command buffer, as well as the cursor position.
|
||||
|
||||
\param b the new buffer value
|
||||
\param p the cursor position. If \c p is less than zero, the cursor is placed on the last character.
|
||||
\param p the cursor position. If \c p is larger than the length of the command line,
|
||||
the cursor is placed on the last character.
|
||||
*/
|
||||
void reader_set_buffer( const wcstring &b, int p );
|
||||
void reader_set_buffer( const wcstring &b, size_t p );
|
||||
|
||||
/**
|
||||
Get the current cursor position in the command line. If interactive
|
||||
mode is uninitialized, return -1.
|
||||
mode is uninitialized, return (size_t)(-1).
|
||||
*/
|
||||
int reader_get_cursor_pos();
|
||||
size_t reader_get_cursor_pos();
|
||||
|
||||
/**
|
||||
Return the value of the interrupted flag, which is set by the sigint
|
||||
@@ -145,8 +146,8 @@ void reader_set_complete_function( complete_function_t );
|
||||
/**
|
||||
The type of a highlight function.
|
||||
*/
|
||||
class env_vars;
|
||||
typedef void (*highlight_function_t)( const wcstring &, std::vector<int> &, int, wcstring_list_t *, const env_vars &vars );
|
||||
class env_vars_snapshot_t;
|
||||
typedef void (*highlight_function_t)( const wcstring &, std::vector<int> &, size_t, wcstring_list_t *, const env_vars_snapshot_t &vars );
|
||||
|
||||
/**
|
||||
Specify function for syntax highlighting. The function must take these arguments:
|
||||
@@ -168,7 +169,17 @@ void reader_set_test_function( int (*f)( const wchar_t * ) );
|
||||
Specify string of shell commands to be run in order to generate the
|
||||
prompt.
|
||||
*/
|
||||
void reader_set_prompt( const wchar_t *prompt );
|
||||
void reader_set_left_prompt( const wcstring &prompt );
|
||||
|
||||
/**
|
||||
Specify string of shell commands to be run in order to generate the
|
||||
right prompt.
|
||||
*/
|
||||
void reader_set_right_prompt( const wcstring &prompt );
|
||||
|
||||
|
||||
/** Sets whether autosuggesting is allowed. */
|
||||
void reader_set_allow_autosuggesting(bool flag);
|
||||
|
||||
/**
|
||||
Returns true if the shell is exiting, 0 otherwise.
|
||||
|
||||
17
runbash.sh
17
runbash.sh
@@ -1,17 +0,0 @@
|
||||
#!/bin/bash -l
|
||||
|
||||
bash_config=~/.config/fish/bash_config.fish
|
||||
if [ -e $bash_config ]
|
||||
then
|
||||
mv $bash_config $bash_config.backup
|
||||
fi
|
||||
touch $bash_config
|
||||
echo "function set_default" >> $bash_config
|
||||
echo " if not set -q \$argv[1]" >> $bash_config
|
||||
echo " set -gx \$argv" >> $bash_config
|
||||
echo " end" >> $bash_config
|
||||
echo "end" >> $bash_config
|
||||
echo "PS1=$PS1" | python share/tools/import_bash_settings.py
|
||||
alias | python share/tools/import_bash_settings.py
|
||||
env | python share/tools/import_bash_settings.py
|
||||
|
||||
@@ -60,7 +60,7 @@ void validate_pointer( const void *ptr, const wchar_t *err, int null_ok )
|
||||
Test if the pointer data crosses a segment boundary.
|
||||
*/
|
||||
|
||||
if( (0x00000003l & (long)ptr) != 0 )
|
||||
if( (0x00000003l & (intptr_t)ptr) != 0 )
|
||||
{
|
||||
debug( 0, _(L"The pointer '%ls' is invalid"), err );
|
||||
sanity_lose();
|
||||
|
||||
985
screen.cpp
985
screen.cpp
File diff suppressed because it is too large
Load Diff
140
screen.h
140
screen.h
@@ -14,38 +14,46 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
struct line_entry_t
|
||||
{
|
||||
wchar_t text;
|
||||
int color;
|
||||
};
|
||||
|
||||
/**
|
||||
A class representing a single line of a screen.
|
||||
*/
|
||||
class line_t
|
||||
struct line_t
|
||||
{
|
||||
public:
|
||||
std::vector<struct line_entry_t> entries;
|
||||
std::vector<wchar_t> text;
|
||||
std::vector<int> colors;
|
||||
bool is_soft_wrapped;
|
||||
|
||||
void resize(size_t size) {
|
||||
entries.resize(size);
|
||||
line_t() : text(), colors(), is_soft_wrapped(false)
|
||||
{
|
||||
}
|
||||
|
||||
line_entry_t &entry(size_t idx) {
|
||||
return entries.at(idx);
|
||||
void clear(void)
|
||||
{
|
||||
text.clear();
|
||||
colors.clear();
|
||||
}
|
||||
|
||||
line_entry_t &create_entry(size_t idx) {
|
||||
if (idx >= entries.size()) {
|
||||
entries.resize(idx + 1);
|
||||
}
|
||||
return entries.at(idx);
|
||||
void append(wchar_t txt, int color)
|
||||
{
|
||||
text.push_back(txt);
|
||||
colors.push_back(color);
|
||||
}
|
||||
|
||||
size_t size(void) const
|
||||
{
|
||||
return text.size();
|
||||
}
|
||||
|
||||
size_t entry_count(void) {
|
||||
return entries.size();
|
||||
wchar_t char_at(size_t idx) const
|
||||
{
|
||||
return text.at(idx);
|
||||
}
|
||||
|
||||
int color_at(size_t idx) const
|
||||
{
|
||||
return colors.at(idx);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -57,7 +65,12 @@ class screen_data_t
|
||||
|
||||
public:
|
||||
|
||||
int cursor[2];
|
||||
struct cursor_t {
|
||||
int x;
|
||||
int y;
|
||||
cursor_t() : x(0), y(0) { }
|
||||
cursor_t(int a, int b) : x(a), y(b) { }
|
||||
} cursor;
|
||||
|
||||
line_t &add_line(void) {
|
||||
line_datas.resize(line_datas.size() + 1);
|
||||
@@ -67,7 +80,7 @@ class screen_data_t
|
||||
void resize(size_t size) {
|
||||
line_datas.resize(size);
|
||||
}
|
||||
|
||||
|
||||
line_t &create_line(size_t idx) {
|
||||
if (idx >= line_datas.size()) {
|
||||
line_datas.resize(idx + 1);
|
||||
@@ -90,34 +103,47 @@ class screen_data_t
|
||||
class screen_t
|
||||
{
|
||||
public:
|
||||
/**
|
||||
The internal representation of the desired screen contents.
|
||||
*/
|
||||
screen_data_t desired;
|
||||
/**
|
||||
The internal representation of the actual screen contents.
|
||||
*/
|
||||
screen_data_t actual;
|
||||
|
||||
/**
|
||||
A string containing the prompt which was last printed to
|
||||
the screen.
|
||||
*/
|
||||
wcstring actual_prompt;
|
||||
/** Constructor */
|
||||
screen_t();
|
||||
|
||||
/**
|
||||
The actual width of the screen at the time of the last screen
|
||||
write.
|
||||
*/
|
||||
int actual_width;
|
||||
/**
|
||||
The internal representation of the desired screen contents.
|
||||
*/
|
||||
screen_data_t desired;
|
||||
/**
|
||||
The internal representation of the actual screen contents.
|
||||
*/
|
||||
screen_data_t actual;
|
||||
|
||||
/**
|
||||
/**
|
||||
A string containing the prompt which was last printed to
|
||||
the screen.
|
||||
*/
|
||||
wcstring actual_left_prompt;
|
||||
|
||||
/** Last right prompt width */
|
||||
size_t last_right_prompt_width;
|
||||
|
||||
/**
|
||||
The actual width of the screen at the time of the last screen
|
||||
write.
|
||||
*/
|
||||
int actual_width;
|
||||
|
||||
/** If we support soft wrapping, we can output to this location without any cursor motion. */
|
||||
screen_data_t::cursor_t soft_wrap_location;
|
||||
|
||||
/**
|
||||
This flag is set to true when there is reason to suspect that
|
||||
the parts of the screen lines where the actual content is not
|
||||
filled in may be non-empty. This means that a clr_eol command
|
||||
has to be sent to the terminal at the end of each line.
|
||||
*/
|
||||
int need_clear;
|
||||
bool need_clear;
|
||||
|
||||
/** If we need to clear, this is how many lines the actual screen had, before we reset it. This is used when resizing the window larger: if the cursor jumps to the line above, we need to remember to clear the subsequent lines. */
|
||||
size_t actual_lines_before_reset;
|
||||
|
||||
/**
|
||||
These status buffers are used to check if any output has occurred
|
||||
@@ -132,13 +158,34 @@ class screen_t
|
||||
will use it's knowlege of the current contents of the screen in
|
||||
order to render the desired output using as few terminal commands
|
||||
as possible.
|
||||
|
||||
\param s the screen on which to write
|
||||
\param left_prompt the prompt to prepend to the command line
|
||||
\param right_prompt the right prompt, or NULL if none
|
||||
\param commandline the command line
|
||||
\param explicit_len the number of characters of the "explicit" (non-autosuggestion) portion of the command line
|
||||
\param colors the colors to use for the comand line
|
||||
\param indent the indent to use for the command line
|
||||
\param cursor_pos where the cursor is
|
||||
*/
|
||||
void s_write( screen_t *s,
|
||||
const wchar_t *prompt,
|
||||
const wchar_t *left_prompt,
|
||||
const wchar_t *right_prompt,
|
||||
const wchar_t *commandline,
|
||||
size_t explicit_len,
|
||||
const int *colors,
|
||||
const int *indent,
|
||||
int cursor_pos );
|
||||
size_t cursor_pos );
|
||||
|
||||
|
||||
void s_write( screen_t *s,
|
||||
const wcstring &left_prompt,
|
||||
const wcstring &right_prompt,
|
||||
const wcstring &commandline,
|
||||
size_t explicit_len,
|
||||
const int *colors,
|
||||
const int *indent,
|
||||
size_t cursor_pos );
|
||||
|
||||
/**
|
||||
This function resets the screen buffers internal knowledge about
|
||||
@@ -146,7 +193,8 @@ void s_write( screen_t *s,
|
||||
function than s_write has written to the screen.
|
||||
|
||||
\param s the screen to reset
|
||||
\param reset_cursor whether the line on which the curor has changed should be assumed to have changed. If \c reset_cursor is set to 0, the library will attempt to make sure that the screen area does not seem to move up or down on repaint.
|
||||
\param reset_cursor whether the line on which the curor has changed should be assumed to have changed. If \c reset_cursor is false, the library will attempt to make sure that the screen area does not seem to move up or down on repaint.
|
||||
\param Whether to reset the prompt as well. If so
|
||||
|
||||
If reset_cursor is incorreclt set to 0, this may result in screen
|
||||
contents being erased. If it is incorrectly set to one, it may
|
||||
@@ -155,6 +203,6 @@ void s_write( screen_t *s,
|
||||
resizing, there will be one line of garbage for every repaint,
|
||||
which will quicly fill the screen.
|
||||
*/
|
||||
void s_reset( screen_t *s, bool reset_cursor );
|
||||
void s_reset( screen_t *s, bool reset_cursor, bool reset_prompt = true );
|
||||
|
||||
#endif
|
||||
|
||||
@@ -58,6 +58,10 @@
|
||||
*/
|
||||
#define GETOPT_STRING "b:hvocu"
|
||||
|
||||
#ifdef _
|
||||
#undef _
|
||||
#endif
|
||||
|
||||
#ifdef USE_GETTEXT
|
||||
#define _(string) gettext(string)
|
||||
#else
|
||||
@@ -94,35 +98,6 @@ const int col_idx[]=
|
||||
8
|
||||
};
|
||||
|
||||
int translate_color( char *str )
|
||||
{
|
||||
char *endptr;
|
||||
int color;
|
||||
|
||||
if( !str )
|
||||
return -1;
|
||||
|
||||
errno = 0;
|
||||
color = strtol( str, &endptr, 10 );
|
||||
|
||||
if( *endptr || color<0 || errno )
|
||||
{
|
||||
size_t i;
|
||||
color = -1;
|
||||
for( i=0; i<COLORS; i++ )
|
||||
{
|
||||
|
||||
if( strcasecmp( col[i], str ) == 0 )
|
||||
{
|
||||
color = col_idx[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return color;
|
||||
|
||||
}
|
||||
|
||||
void print_colors()
|
||||
{
|
||||
size_t i;
|
||||
@@ -207,6 +182,10 @@ static unsigned char index_for_color(rgb_color_t c) {
|
||||
|
||||
int main( int argc, char **argv )
|
||||
{
|
||||
/* Some code passes variables to set_color that don't exist, like $fish_user_whatever. As a hack, quietly return failure. */
|
||||
if (argc <= 1)
|
||||
return EXIT_FAILURE;
|
||||
|
||||
char *bgcolor=0;
|
||||
char *fgcolor=0;
|
||||
bool bold=false;
|
||||
|
||||
1
share/completions/..fish
Normal file
1
share/completions/..fish
Normal file
@@ -0,0 +1 @@
|
||||
complete -c . -x -a "(__fish_complete_suffix .fish)"
|
||||
16
share/completions/abook.fish
Normal file
16
share/completions/abook.fish
Normal file
@@ -0,0 +1,16 @@
|
||||
|
||||
complete -c abook -s h -d 'Show usage'
|
||||
complete -c abook -s C -l config -d 'Use an alternative configuration file' -r
|
||||
complete -c abook -l datafile -d 'Use an alternative addressbook file' -r
|
||||
complete -c abook -l mutt-query -d 'Make a query for mutt' -x
|
||||
complete -c abook -l add-email -d 'Read email message from stdin and add the sender'
|
||||
complete -c abook -l add-email-quiet -d 'Same as --add-email. Without confirmation'
|
||||
complete -c abook -l convert -d 'Convert address book files'
|
||||
|
||||
set -l convert 'contains -- --convert (commandline -po)'
|
||||
complete -c abook -l informat -d 'Input file format' -xa '(__fish_complete_abook_formats in)' -n $convert
|
||||
complete -c abook -l outformat -d 'Output file format' -xa '(__fish_complete_abook_formats out)' -n $convert
|
||||
complete -c abook -l infile -d 'Input file (default: stdin)' -r -n $convert
|
||||
complete -c abook -l outfile -d 'Output file (default: stdout)' -r -n $convert
|
||||
|
||||
complete -c abook -l formats -d 'Print available formats'
|
||||
1
share/completions/acat.fish
Normal file
1
share/completions/acat.fish
Normal file
@@ -0,0 +1 @@
|
||||
__fish_complete_atool acat
|
||||
3
share/completions/accept.fish
Normal file
3
share/completions/accept.fish
Normal file
@@ -0,0 +1,3 @@
|
||||
__fish_complete_lpr accept
|
||||
complete -c accept -s r -d 'Accept reason' -x
|
||||
|
||||
1
share/completions/adiff.fish
Normal file
1
share/completions/adiff.fish
Normal file
@@ -0,0 +1 @@
|
||||
__fish_complete_atool adiff
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user