Compare commits

..

285 Commits

Author SHA1 Message Date
ridiculousfish
5827bfd9a2 Default fish_pager_color_description to '555 yellow'
Fixes https://github.com/fish-shell/fish-shell/issues/280
2012-11-18 03:17:51 -08:00
ridiculousfish
dc8cc39bc8 Don't autosuggest in builtin_read
https://github.com/fish-shell/fish-shell/issues/29
2012-11-18 02:43:35 -08:00
ridiculousfish
c9c2fc5ee3 Restore terminal foreground process group on exit
Fixes https://github.com/fish-shell/fish-shell/issues/197
2012-11-18 02:16:14 -08:00
Terje Larsen
b79854ad1a Optimize staged logic 2012-11-17 13:37:32 -08:00
Terje Larsen
8229b17f0d Use double-dollar vars for dynamic variables 2012-11-17 13:37:32 -08:00
Terje Larsen
4a5d02aab4 Add configurable status signs again 2012-11-17 13:37:32 -08:00
Terje Larsen
ed4e6cd3a5 Fix uniqueness and escape question-marks 2012-11-17 13:37:32 -08:00
Sławek Piotrowski
7d45e6f12f Fix: args -> argv in __fish_git_prompt 2012-11-14 19:00:17 +01:00
ridiculousfish
0302162d8e Make tab-completion truncation less dumb
Fixes https://github.com/fish-shell/fish-shell/issues/259
2012-11-09 16:02:19 -08:00
ridiculousfish
1dfa404210 Revert "Another attempt to improve right prompt on Linux"
It didn't work

This reverts commit bd4551e2ef.
2012-11-09 15:21:08 -08:00
ridiculousfish
bd4551e2ef Another attempt to improve right prompt on Linux 2012-11-09 15:19:54 -08:00
ridiculousfish
d6a56428d1 Attempt to fix fish_right_prompt under Linux 2012-11-09 15:09:54 -08:00
David Adam (zanchey)
a10fa3a20b add $FISH_VERSION as unexported global variable to complement version 2012-11-08 10:09:51 -08:00
David Adam (zanchey)
be5fde9e54 test documentation - format examples properly 2012-11-08 10:08:06 -08:00
David Adam (zanchey)
abfc7e16a1 test documentation - correct argument listing 2012-11-08 10:08:06 -08:00
ridiculousfish
d76f880faf Support for fish_right_prompt
Fixes https://github.com/fish-shell/fish-shell/issues/80
2012-11-07 19:59:20 -08:00
ridiculousfish
21e83a881e Bring back ellipsis 2012-11-05 00:05:42 -08:00
ridiculousfish
5ba1261285 Initial right_prompt work 2012-11-04 23:21:37 -08:00
ridiculousfish
7bb844a778 Fix bug where 'else if' does not support functions and redirections
https://github.com/fish-shell/fish-shell/issues/359
2012-11-04 17:11:02 -08:00
ridiculousfish
8c24d49c73 Removed unused commented out code and fixed some indentation 2012-11-04 15:47:55 -08:00
ridiculousfish
5e371e8fe7 Don't use posix_spawn for commands that need to be put into foreground to avoid a race
Fix for race where a command's output may not be fully drained
2012-11-04 15:45:52 -08:00
ridiculousfish
e46324ced9 Wrong sense of check for initializing key bindings
Fix for https://github.com/fish-shell/fish-shell/pull/373
2012-10-30 14:36:37 -07:00
ridiculousfish
5ea486b20a Fix help completion to not barf if html file is absent 2012-10-29 01:53:10 -07:00
ridiculousfish
425afa63ce Don't use posix_spawn when file redirections are involved (except /dev/null) because the error handling is too difficult
Fix exec to correctly handle the case where a pid could not be created due to posix_spawn failing
Should fix https://github.com/fish-shell/fish-shell/issues/364
2012-10-29 01:45:51 -07:00
ridiculousfish
7c09a767b6 Don't expand jobs during syntax highlighting
Fixes https://github.com/fish-shell/fish-shell/issues/366
2012-10-28 16:35:17 -07:00
Siteshwar Vashisht
768f92303e Removed python scripts to import bash settings 2012-10-27 23:57:15 +05:30
ridiculousfish
34054fa1fb Merge pull request #362 from terlar/git-completions
Git completions
2012-10-25 18:56:59 -07:00
Terje Larsen
4a37d6d1f9 Remove remotes prefix from branches 2012-10-22 20:54:33 +02:00
Terje Larsen
8dfc8625a7 Add git completions for tracking (branch/checkout) 2012-10-22 20:50:38 +02:00
Terje Larsen
e878947cb2 Fix broken git alias completion
The previous command outputs a lot of junk, does not strip after the
white-space in OSX (10.8.2).

Tried out the new command on both Ubuntu (12.04.1) and OSX (10.8.2)
2012-10-22 20:37:47 +02:00
Terje Larsen
f56f84c6ac Use echo -n instead of printf
- Remove useless comment, because multi-line prompt works good now.
2012-10-20 12:52:53 -07:00
Terje Larsen
239d43dac4 Improve the git prompt
- Fix branch for older git version (--short for symbolic-ref was not
  available on git 1.7.9.5)
- Use index (git status) for checking if staged
- Add status indication for copied
- Remove variables for statuses (less litter in the variables)
- Remove usage of eval to echo and set_color
- Replace printf where possible with echo -n
2012-10-20 12:52:53 -07:00
ridiculousfish
8a63326411 Removed backwards compatibility check for fish_user_keybindings 2012-10-20 12:21:41 -07:00
Nate Soares
01608cf062 Normalized some inconsistent whitespace. 2012-10-20 12:15:49 -07:00
Nate Soares
e06d6ce4a2 Revive support for fish_user_keybindings
This is undocumented and here to preserve backwards compatibility.
2012-10-20 12:15:49 -07:00
Nate Soares
58a0c6f9d3 added missing underscore 2012-10-20 12:15:49 -07:00
Nate Soares
f310f1e96c keybindings → key_bindings (for consistency) 2012-10-20 12:15:49 -07:00
ridiculousfish
c7bf1aa222 Handle some more escapes in de-groffing man pages
Fixes https://github.com/fish-shell/fish-shell/issues/10
2012-10-17 18:22:57 -07:00
Adrien
be3fff9282 Add apt-cache completion for package names
Signed-off-by: Gustavo Noronha Silva <kov@kov.eti.br>
2012-10-17 18:05:49 -07:00
ridiculousfish
e52cf09bc1 Simplify handling of escape sequences in echo -e 2012-10-17 17:08:45 -07:00
ridiculousfish
57de1388e8 Make the \c special character in echo suppress the newline too 2012-10-17 02:59:43 -07:00
ridiculousfish
7ee0ce745e Implement -e option to echo (to interpret special characters), and -E to not interpret them
https://github.com/fish-shell/fish-shell/issues/337
2012-10-17 02:56:03 -07:00
ridiculousfish
469743cd23 Made set_color with no arguments not complain, so that prompts like Terlar don't produce errors for missing colors. 2012-10-17 01:25:21 -07:00
ridiculousfish
91e1d59869 Fix for issue where else if would fail to pass arguments to commands. Also implements short-circuiting for and/or so that non-existent commands don't produce error messages.
Fixes https://github.com/fish-shell/fish-shell/issues/345
Fixes https://github.com/fish-shell/fish-shell/issues/349
2012-10-17 01:07:34 -07:00
ridiculousfish
5fb97f05a3 Fixed "Expansion not correctly handling literal path components in dotfiles" test 2012-10-16 11:51:36 -07:00
ridiculousfish
618b42980d Prevent multi-line prompts from repeating during window resize
Fixes https://github.com/fish-shell/fish-shell/issues/321
2012-10-15 19:25:56 -07:00
ridiculousfish
211b9ea8b9 Added terlar's prompt, and robbyrussell which was inadvertently omitted
https://github.com/fish-shell/fish-shell/issues/329
2012-10-15 18:45:46 -07:00
ridiculousfish
833abc27cc Make wildcards beginning with dots not match . and ..
https://github.com/fish-shell/fish-shell/issues/270
2012-10-15 18:16:47 -07:00
ridiculousfish
3d5a3f03fa Fix to prevent buff_pos from underflowing in reader_kill 2012-10-08 23:47:32 -07:00
ridiculousfish
51de26960c Make escaping consistent for fish <-> fishd protocol
Fix fork guards to work in fishd
https://github.com/fish-shell/fish-shell/issues/339
2012-10-08 14:47:25 -07:00
ridiculousfish
b67526aae8 Don't reset the terminal mode in certain circumstances. Fixes issue in Linux with e.g. echo foo ; ftp
Introduce patch from 9d229cd18c
2012-10-05 18:23:38 -07:00
ridiculousfish
14bf057c62 Remove bogus path from example of "." (source) command
https://github.com/fish-shell/fish-shell/issues/297
2012-10-04 14:57:23 -07:00
ridiculousfish
8eb53ea7ca Rewrite kill behavior (aka control-W) to do something better
Fixes https://github.com/fish-shell/fish-shell/issues/327
2012-10-04 14:35:03 -07:00
ridiculousfish
9b60af4d52 Fix to propagate the last color before highlighting completes, and to avoid repainting when highlighting completes if the color does not change 2012-10-03 12:19:27 -07:00
SanskritFritz
956f99365a Some new completions and modified some others. 2012-10-02 17:54:30 -07:00
SanskritFritz
9904415144 Facelift for pacman completions. 2012-10-02 17:54:30 -07:00
SanskritFritz
83febbdc7d Package groups were completed as repos. Fixed.
By default fish expands arguments along with options. This is not desired,
so made sure they are expanded only when needed, for performance reasons.
2012-10-02 17:54:30 -07:00
ridiculousfish
6b026e399c Prevent some cases where garbage gets dumped on the screen during window resize under new soft wrapping architecture 2012-10-02 17:30:07 -07:00
ridiculousfish
a661c03743 Work on soft wrapping to address https://github.com/fish-shell/fish-shell/issues/300
Resizing is still wonky
2012-10-01 03:29:18 -07:00
Ian Munsie
ffc03735e8 Don't barf when ending a FAKE block
After updating, fish would immediately die when started in an xterm from
my window manager (wmii) with a message like:

fish: builtin.cpp:3357: int builtin_end(parser_t&, wchar_t**): Assertion `false' failed.
fish: Job 1, “~/code/fish-shell/fish” terminated by signal SIGABRT (Abort)

Starting fish from an existing shell worked, and running it on other
machines that I was only SSHing into worked as well. I did discover that
on one machine starting git from one specific directory
(/home/ian/bml/kernel) would cause this failure, but starting from any
other directory worked as normal. I'm not entirely sure what
circumstances contribute to this failure - starting from a copy of the
kernel directory works fine.

The failure only started with the following commit, which introduced the
assert(false):

commit d788c84440
Made type property of block_t constant and private
Further work towards cleaning up block_t hierarchy

Looking at this fail in gdb:

(gdb) r
Starting program: /home/ian/code/fish-shell/fish
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
fish: builtin.cpp:3357: int builtin_end(parser_t&, wchar_t**): Assertion `false' failed.

Program received signal SIGABRT, Aborted.
0x00007ffff6c82475 in *__GI_raise (sig=<optimized out>) at ../nptl/sysdeps/unix/sysv/linux/raise.c:64
64      ../nptl/sysdeps/unix/sysv/linux/raise.c: No such file or directory.
(gdb) bt
    function=0x548520 "int builtin_end(parser_t&, wchar_t**)") at assert.c:81
(gdb) up 3
3357                    assert(false); //should never get here
(gdb) p parser.current_block->type()
$1 = FAKE

So this happens when we run an end command for a FAKE block.

The below patch adds an empty case for FAKE blocks to avoid hitting the
assert. I would need to study the code in more detail to understand if
we should even be executing this code if we are in a FAKE block, but
this patch seems to solve the issue for me.

Signed-off-by: Ian Munsie <darkstarsword@gmail.com>
2012-09-22 23:51:41 -07:00
maxfl
c7e24488eb fix output 2012-09-22 23:49:26 -07:00
maxfl
fe7d2c57e3 nmcli completions 2012-09-22 23:49:26 -07:00
Siteshwar Vashisht
4fc80b5057 Fix for displaying multiline prompts 2012-09-20 00:47:31 +05:30
Siteshwar Vashisht
12f3f5dcbb Print autocompletion description after installing
Print autocompletion shortcut description at end of 'make install'
2012-09-19 13:21:49 +05:30
Siteshwar Vashisht
16f2ffc29d Remove ellipsis and newlines from long lines
Fix for issue https://github.com/fish-shell/fish-shell/issues/300
2012-09-18 00:42:11 +05:30
Peter Ammon
86a978d9ee Fixed web_config prompt to work with Python 2.6.1 2012-09-10 02:11:06 -07:00
ridiculousfish
f6fe3df59b Fix to make prompt chooser work in Python3 2012-09-06 19:01:07 -07:00
ridiculousfish
e0764bb25e Improve python3 compatibility in webconfig.py 2012-09-06 02:03:21 -07:00
ridiculousfish
1ba0bfd10c Renamed hyperminamlist prompt 2012-09-06 01:52:21 -07:00
ridiculousfish
593ab75a76 Fix to make Makefile copy sample prompts 2012-09-06 01:40:15 -07:00
ridiculousfish
3589554028 Allow setting the prompt from web_config 2012-09-06 01:30:26 -07:00
ridiculousfish
1a59346b51 Changed "elseif" to "else if" 2012-09-03 13:24:01 -07:00
ridiculousfish
ff124465fd Clean up some warnings and some unused if-related code 2012-09-01 12:29:00 -07:00
ridiculousfish
de5223db66 Improve documentation and error reporting for elseif. 2012-09-01 02:14:13 -07:00
ridiculousfish
cc1395797e First stab at elseif implementation 2012-09-01 01:46:14 -07:00
Torsten Grust
122791646e Fix check for non-empty argument (= initial tab) in fish_config
The original version (based on 'test') was creating spurious files named "0" in the current working directory
2012-08-30 13:17:32 +02:00
ridiculousfish
a3c4de52d6 Fix braces style 2012-08-26 23:34:34 -07:00
ridiculousfish
95de6cf5a7 Migrated function_data_t out of base block class
Removed an auto_ptr (yay)
2012-08-26 23:30:23 -07:00
ridiculousfish
d788c84440 Made type property of block_t constant and private
Further work towards cleaning up block_t hierarchy
2012-08-26 23:16:20 -07:00
ridiculousfish
96046ffd30 First stab at getting rid of the ugly state1, state2 properties of block_t 2012-08-26 22:42:29 -07:00
ridiculousfish
b5e92a831b Use xdg-open instead of BROWSER, if available
https://github.com/fish-shell/fish-shell/issues/293
2012-08-25 13:02:58 -07:00
maxfl
a2788129ff Minor updates
* Add -L/--long completion for 'set' command.
* Fix completion description color.
2012-08-25 00:54:47 -07:00
ridiculousfish
5bbf220077 Fix bug where underlining was failing for paths prefixed with ~
Fixes https://github.com/fish-shell/fish-shell/issues/292
2012-08-23 11:21:35 -07:00
ridiculousfish
f5d4e3f94c Reintroduce IO transmorgrification (yuck) to fix problems with fish_config and complicated IO redirections 2012-08-22 13:41:21 -07:00
ridiculousfish
04ea680e9a Support for tab cyling through completions
https://github.com/fish-shell/fish-shell/issues/84
2012-08-21 17:18:52 -07:00
ridiculousfish
3a940781d9 Replaced README with README.md for github
Changed 'root' Xcode target to 'install_tree' to avoid confusion
2012-08-21 02:08:49 -07:00
ridiculousfish
00fcd63b88 Clean up fish.app target to build correctly. Still won't run from Xcode, but will run from Finder 2012-08-21 01:20:19 -07:00
ridiculousfish
f3093649de Improve Xcode build process to provide a real target for xcodebuild install 2012-08-20 23:50:11 -07:00
ridiculousfish
670e33ab27 Properly handle empty completions (e.g. tab-complete 'foo' with extant files 'foo' and 'foobar' should offer both)
Fixes issue described in https://github.com/fish-shell/fish-shell/issues/290
2012-08-20 13:09:21 -07:00
ridiculousfish
3606664cf7 Merge branch 'webserver' of git://github.com/simukis/fish-shell into simukis-webserver
Conflicts:
	share/tools/web_config/webconfig.py
2012-08-20 12:03:39 -07:00
ridiculousfish
81f45208b0 Make history deletion from web config work properly with Unicode under both Python2 and Python3
Make the filter search field hide properly in tabs where it's non-functional
Fixes https://github.com/fish-shell/fish-shell/issues/265
2012-08-20 11:58:54 -07:00
ridiculousfish
9145d05397 Restore correct order of IO redirections
Fixes https://github.com/fish-shell/fish-shell/issues/281
2012-08-19 14:09:39 -07:00
Simonas Kazlauskas
ef566836c4 Wipe unnecessary as statement 2012-08-19 23:26:15 +03:00
Simonas Kazlauskas
10dfca1a75 Decode data in python3 – Fixes #265.
In both in python2 and python3 parse_qs expects str object. In
python2 it worked ok, because self.rfile was open in binary mode and
str in python2 is actually a string of bytes. However in python3 str is
actually string of unicode literals, not bytes and file was still open
in binary mode. Thus, deleting any file with non-ascii byte inside
filename failed in python3.

Also, cgi.parse_qs is deprecated and shouldn't be used.
2012-08-19 23:19:07 +03:00
Simonas Kazlauskas
b3e3f041fe Remove trailing spaces and replace tabs with spaces 2012-08-19 22:55:50 +03:00
ridiculousfish
e3ec361552 Fixed indentation in next_tab_stop 2012-08-17 17:41:55 -07:00
ridiculousfish
bfccc7133d Fix for issue where init_tabs can't be modified on NetBSD 2012-08-17 17:36:52 -07:00
ridiculousfish
f41a699f5d Fix to make the choose-a-port loop work correctly for webconfig.py under Python3 2012-08-17 01:14:05 -07:00
ridiculousfish
7d029778e6 Disable file completion descriptions per https://github.com/fish-shell/fish-shell/issues/279 2012-08-15 18:20:44 -07:00
ridiculousfish
26857fabdc Real fix for https://github.com/fish-shell/fish-shell/issues/278
We forget to set the parent group ID in posix_spawn (!)
2012-08-15 17:32:57 -07:00
ridiculousfish
a9b119833a Actually work around https://github.com/fish-shell/fish-shell/issues/278 2012-08-15 17:26:54 -07:00
ridiculousfish
c5ffe8a974 Temporary workaround for SIGTIN and SIGTOU unhappiness with posix_spawn
Fixes https://github.com/fish-shell/fish-shell/issues/278
2012-08-15 17:25:33 -07:00
ridiculousfish
df1b4e1f21 Fix for missing copy-files phase in Xcode build 2012-08-15 16:54:30 -07:00
ridiculousfish
61686aff34 Adopt posix_spawn (!)
Rewrite IO chains to be a vector of pointers, instead of a linked list
Removed io_transmogrify
2012-08-15 00:57:56 -07:00
Scott Leggett
ad6645c48d Implement completion for 'dd'.
Closes #267.
2012-08-12 22:05:12 +10:00
ridiculousfish
1e328c3546 Better handle symlink loops in recursive wildcards (**)
https://github.com/fish-shell/fish-shell/issues/268
2012-08-07 02:50:12 -07:00
ridiculousfish
0e2a625815 Added some limited support for autosuggesting processes 2012-08-07 00:01:48 -07:00
ridiculousfish
6eb66770a4 Fix to make completions non-authoritative by default, which is why unknown options were always colored like errors (e.g. --rebase) 2012-08-06 23:34:55 -07:00
ridiculousfish
84729c4dfa Additional warnings cleanup, effective C++ violations, dead code removal 2012-08-05 13:24:33 -07:00
ridiculousfish
8de8877c7c Fix OS X compilation 2012-08-05 12:05:05 -07:00
ridiculousfish
fdc6c3722a Fixed a bunch of clang analyzer warnings
Simplified some memory allocations by migrating to std::string
2012-08-05 12:01:53 -07:00
Siteshwar Vashisht
deca475972 Fixed compilation on Linux
Addresses issue https://github.com/fish-shell/fish-shell/issues/264
2012-08-05 18:37:51 +05:30
ridiculousfish
ba070e21e4 Fix for stack overflow when overflowing a line 2012-08-04 18:41:14 -07:00
ridiculousfish
35e9fd1bb0 Fix for weird issues when a line becomes very long introduced by my warning fixes 2012-08-04 18:32:15 -07:00
ridiculousfish
4906609dd9 Fix for assertion failure in syntax highlighting 2012-08-04 18:02:13 -07:00
ridiculousfish
682353f9cc Fix to restore an optimization from parse_util_get_line_from_offset in a more thread-safe way 2012-08-04 17:44:14 -07:00
ridiculousfish
25c6671a87 Fix for inability to go backwards through history 2012-08-04 16:06:40 -07:00
ridiculousfish
c67702a498 Cleaned up lots of typecasts, simplified some string handling 2012-08-04 15:11:43 -07:00
ridiculousfish
5880cd88c8 Switch from int cursor[2] to struct cursor { int x; int y; } 2012-08-04 13:54:20 -07:00
ridiculousfish
54ceb4211e Additional warning fixes and migration from int to size_t or long where appropriate 2012-08-04 13:47:56 -07:00
ridiculousfish
7a46227141 More warning fixes and switching from int to long or size_t 2012-08-04 13:02:44 -07:00
ridiculousfish
b904aa78e8 Additional warning cleanup and switching from int to size_t where appropriate 2012-08-04 11:34:45 -07:00
ridiculousfish
8185bee4b8 Lots of work towards making fish build without warnings on Mountain Lion, mostly in terms of using size_t instead of int 2012-08-04 11:34:45 -07:00
ridiculousfish
2e1b3325c6 Warning cleanup 2012-08-04 11:34:45 -07:00
Rubycut
1b8f1650dc explain how to load binds automatically 2012-08-04 11:32:04 -07:00
maxfl
1070b34996 Get rid of 'nothing appropriate' output when completing.
Add watch completion.
2012-08-04 11:20:03 -07:00
Anders Bergh
13b1b73c15 Replaced references to http://fishshell.org/ with http://ridiculousfish.com/shell/. 2012-08-01 18:09:50 +02:00
ridiculousfish
e1de72d0ac Merge pull request #251 from bpinto/rbenv
completion for rbenv
2012-07-28 17:50:27 -07:00
Bruno Pinto
dc837eb8a3 completion for rbenv 2012-07-28 13:45:47 -03:00
ridiculousfish
f6b76e6ecb Fix to allow specifying an initial tab in fish_config
For example, you can run "fish_config history"
2012-07-27 13:40:43 -07:00
ridiculousfish
e7cbcc83a4 Implemented history deletion from fish_config
Fixes https://github.com/fish-shell/fish-shell/issues/250
2012-07-27 00:31:00 -07:00
ridiculousfish
390700ca71 Merge pull request #249 from kballard/git_prompt_showupstream
Fix showupstream behavior in __fish_git_prompt
2012-07-27 00:26:23 -07:00
Kevin Ballard
b604321169 Fix showupstream behavior in __fish_git_prompt
The __fish_git_prompt_show_upstream helper function was inadvertently
looking at the misnamed variable __fish_git_prompt_show_upstream in some
cases, including when implementing the bash.showUpstream override.
Fixing the script to use __fish_git_prompt_showupstream triggered an
infinite loop because the --on-variable hook does not distinguish
between local and global variables.

Update the script to set a completely different local variable to
__fish_git_prompt_showupstream and to override this local variable for
bash.showUpstream.

Also update the code that looks at bash.showUpstream to also read
bash.showupstream, because the bash script appears to have a bug where
it looks for bash.showupstream despite documenting bash.showUpstream.
2012-07-26 15:32:27 -07:00
ridiculousfish
62c49f13ce Switch from std::list to std::vector in a few places to reduce compiled code size 2012-07-24 22:32:11 -07:00
Colin Woodbury
eba75dbc2e Fixed two small spelling mistakes
- Saw these during normal usage today.
  "parens" or "parenthesis" was spelled as "parans".
  Fixed two instances of this to "parenthesis".
2012-07-24 22:39:03 +09:00
Siteshwar Vashisht
32d2d0f0d0 Minor refactoring and fixed a bug in history function 2012-07-24 04:35:37 +05:30
maxfl
81e0342bf6 index range doc 2012-07-24 03:56:34 +05:30
ridiculousfish
56599621cc Make add-shell correctly handle /etc/shells files that do not end with newlines
Fixes https://github.com/fish-shell/fish-shell/issues/77
2012-07-22 17:00:44 -07:00
ridiculousfish
261bf12c91 Lots of miscellaneous cleanup. Unified the path_get_cd_path, path_allocate_cd_path, etc. functions 2012-07-20 22:11:05 -07:00
ridiculousfish
b08fb86637 Renamed env_vars to env_vars_snapshot_t
Cleanup of non-wcstring version of path_get_path
2012-07-20 20:39:31 -07:00
ridiculousfish
b290fd33b9 Switch to more uses of wcstokenizer from wcstok()
Work towards cleaning up path_get_path
2012-07-20 15:01:56 -07:00
ridiculousfish
966bbd476f Use weak linking of wcsdup and wcscasecmp on OS X
Fixes https://github.com/fish-shell/fish-shell/issues/240
2012-07-20 14:33:29 -07:00
Siteshwar Vashisht
cf9bfe9e66 Print fish prompt when command is longer than a line
Fix for https://github.com/fish-shell/fish-shell/issues/239
2012-07-21 00:24:48 +05:30
ridiculousfish
bb4a05032b Merge branch 'index_range' 2012-07-19 10:59:11 -07:00
ridiculousfish
7039e01136 Fixed fish_pager build on Xcode (it was building fish_indent instead. D'oh!) 2012-07-18 12:28:26 -07:00
ridiculousfish
e9f43f1097 Changes to make fish use the relocated fishd instead of the installed one, if it exists 2012-07-18 10:50:56 -07:00
ridiculousfish
150789690f Merge pull request #234 from maxfl/completions
pacmatic and dmesg completions
2012-07-18 10:49:28 -07:00
Siteshwar Vashisht
e284233013 Don't add space at the end of tab completion for cd
Fix for https://github.com/fish-shell/fish-shell/issues/235
2012-07-18 19:48:19 +05:30
ridiculousfish
d06d6c6964 Various changes to reduce fish's compiled code size
OS X release build executable size dropped from 672k to 511k
2012-07-17 12:47:01 -07:00
ridiculousfish
977a4477f6 Fix for process completion on Linux 2012-07-16 12:19:41 -07:00
ridiculousfish
33c6410809 Implemented process expansion on OS X
Also fixed issue where process expansion would always fail for processes with spaces
Fixes https://github.com/fish-shell/fish-shell/issues/56
2012-07-16 12:06:02 -07:00
maxfl
7e321afd2a pacmatic and dmesg completions 2012-07-16 10:03:01 +08:00
ridiculousfish
548ea1e54a Added binding for accepting an autosuggestion
Fixes https://github.com/fish-shell/fish-shell/issues/226
2012-07-15 14:02:34 -07:00
ridiculousfish
4755c5f8c8 Deliver SIGHUP when we're exiting, even if stdin is not closed.
Fixes https://github.com/fish-shell/fish-shell/issues/138
2012-07-15 12:46:40 -07:00
ridiculousfish
ea1bfd715e Set of changes to improve Unicode support with respect to combining characters.
Should address https://github.com/fish-shell/fish-shell/issues/155
2012-07-15 10:45:18 -07:00
Kevin Ballard
b1281c3fb9 Update __fish_git_prompt.fish to handle git-svn better
Git-svn remotes can have a prefix for their remotes. If I set a prefix
of 'svn/' then my remote trunk branch is called svn/trunk. Update the
script to use the svn-remote.*.fetch key to figure out how the 'trunk'
branch is mapped into the remotes namespace and apply this to the
current branch. This assumes branches are mapped into the same
namespace, which is likely. It also doesn't work for tags, but neither
did the old code.
2012-07-13 21:46:57 -07:00
Alex Rosenberg
257e9b04fe Add auto-generated builtin_scripts.[cpp|h] to .gitignore 2012-07-13 21:40:11 -07:00
ridiculousfish
8643870822 Tweaked fish_prompt document in a way I hope clarifies it 2012-07-13 21:32:50 -07:00
Dario Bertini
6819696222 Added small note to the fish_prompt doc 2012-07-12 14:52:15 -07:00
ridiculousfish
193bbd4e00 Tweaked pwd note about symbolic links 2012-07-12 14:48:51 -07:00
Dario Bertini
1bf168348e Added doc for pwd 2012-07-12 14:45:37 -07:00
ridiculousfish
f8b4c1b389 First pass at making autosuggestions not trigger wrapping
Addresses https://github.com/fish-shell/fish-shell/issues/167
2012-07-12 12:51:47 -07:00
ridiculousfish
830fba64b1 Fix for https://github.com/fish-shell/fish-shell/issues/42
Remote completions in scp
2012-07-11 17:20:58 -07:00
ridiculousfish
1cd61952b0 Fix for https://github.com/fish-shell/fish-shell/issues/223
Use -rpath instead of -R
2012-07-11 17:18:49 -07:00
ridiculousfish
4ee1cc3b37 Fix for https://github.com/fish-shell/fish-shell/issues/168
Make ^ only act as a redirect at the beginning of a token
2012-07-10 20:30:54 -07:00
maxfl
11dd904b6d Tests for contains -i 2012-07-10 16:21:00 -07:00
maxfl
0c03b6ddc7 Allow 'contains' builtin to return index
Add an option -i/--index to the contains builtin to print the index
of first coincidence.
2012-07-10 16:21:00 -07:00
ridiculousfish
176a3913aa Fix for https://github.com/fish-shell/fish-shell/issues/106
Lets us configure and build on FreeBSD
2012-07-10 15:37:16 -07:00
ridiculousfish
6f0b00f983 Fix for https://github.com/fish-shell/fish-shell/issues/66
Make fish import .bash_history if regular history is not found
2012-07-09 22:54:08 -07:00
ridiculousfish
69ace201f8 Fix for https://github.com/fish-shell/fish-shell/issues/79
Release tarballs with the user docs already build, removing the doxygen dependency
2012-07-09 19:41:51 -07:00
ridiculousfish
a04f31c5c0 Fix for https://github.com/fish-shell/fish-shell/issues/218
make help smarter on OS X (don't show useless builtin man page)
2012-07-09 18:46:14 -07:00
ridiculousfish
1d9f47d1e5 https://github.com/fish-shell/fish-shell/issues/207
Improve error message for infinite loop detection
2012-07-09 15:18:22 -07:00
ridiculousfish
36a91fc6ff Fix for https://github.com/fish-shell/fish-shell/issues/28
Use pthread_sigmask instead of sigprocmask
2012-07-09 14:15:55 -07:00
ridiculousfish
cd3ed71137 Fixes Solaris kludge https://github.com/fish-shell/fish-shell/issues/76 2012-07-08 22:36:04 -07:00
ridiculousfish
73e56527bf Improve error message for $? 2012-07-08 18:51:52 -07:00
ridiculousfish
24059924b1 Fix for https://github.com/fish-shell/fish-shell/issues/50
Unescape characters before calling parser.error
2012-07-08 18:40:50 -07:00
ridiculousfish
bb100a2511 Turn on debugging symbols correctly for OS X build 2012-07-08 18:04:31 -07:00
ridiculousfish
0c8c32cd03 Fix for https://github.com/fish-shell/fish-shell/issues/105
Not sure why the escape character was being printed in the manpage completions script
2012-07-08 17:35:07 -07:00
ridiculousfish
a38d6e74f5 Renamed a target in the OS X build system 2012-07-08 16:51:39 -07:00
ridiculousfish
05d8dacb27 Simplified native OS X build system 2012-07-08 16:17:49 -07:00
ridiculousfish
d6a0859eab Updated doc dir path in fish.cpp 2012-07-08 15:42:47 -07:00
ridiculousfish
4912967eab Large set of changes related to making fish relocatable, and improving the build and install story.
- etc/config.fish and share/config.fish are now "universal" and no longer reference install paths or need to be touched by autotools. They've been removed from config.fish.in to config.fish.
- fish now attempts to determine __fish_datadir and __fish_sysconfdir relative to the path of the fish executable itself (typically by walking up one directory). This means that you can copy the directory hierarchy around and things will still work. The compiled-in paths are used as a backup.
- The fish Xcode project now can build fish natively, without needing autotools.
- Version bumped to 2.0
2012-07-08 15:20:39 -07:00
maxfl
76e1cda495 Set single-line default prompt
Fish now doesn't join the fish_prompt output. This breaks the default
fish_prompt. Make default fish_prompt single-line. Fixes #203.

Add -l flag to 'read' documentation. Remove ambiguous '-x' description.
Fixes #157.
2012-07-07 23:34:16 -07:00
ridiculousfish
c1a23bf450 Fix for https://github.com/fish-shell/fish-shell/issues/199
Don't hard-fail on setting the PATH or CDPATH just because it contains an invalid entry
2012-07-07 23:04:02 -07:00
maxfl
b25b5bf5f6 restore tabs instead of spaces 2012-07-08 10:55:48 +08:00
maxfl
079f17761c Fix case when first index is command substitution 2012-07-08 09:45:34 +08:00
maxfl
e1b8c425da Fix case when second limit is a variable:
echo $PATH[1..$n]
2012-07-08 09:39:39 +08:00
maxfl
5f05756e65 Add variable expand ranges
echo $PATH[-1..1] #now works

Add tests for ranges
2012-07-08 09:19:11 +08:00
maxfl
c0e996acf8 Implemented index ranges for command substitution
Now the following code works:
> echo (seq 10)[-1..1]
With output:
10
9
8
7
6
5
4
3
2
1
2012-07-07 22:01:28 +08:00
maxfl
41ec58a524 misc 2012-07-07 11:04:29 +08:00
maxfl
0a5e7be129 Add index ranges
Builtin 'set' now can set variable index ranges:
  set test[1..3] a b c #works
  set test[-1..-3] a b c #works if variable have enough elements
  set test[2..-2]  a b c #works
  set test[1..3 -1..-2] a b c b b #works

Expand now can parse index ranges. But not handle for now.

TODO:
  * Add variable substitution index ranges: echo $PATH[-1..1]
  * Add command substitution index range: echo (seq 10)[-1..-4]
  * Add process substitution indexes and ranges: echo %vim[-1]
2012-07-07 10:57:28 +08:00
maxfl
b23d65b014 Set single-line default prompt
Fish now doesn't join the fish_prompt output. This breaks the default
fish_prompt. Make default fish_prompt single-line. Fixes #203.

Add -l flag to 'read' documentation. Remove ambiguous '-x' description.
Fixes #157.
2012-07-07 10:17:24 +08:00
ridiculousfish
60ef7903e2 Tweaked __fish_print_help to not spew errors 2012-07-06 16:31:06 -07:00
ridiculousfish
ee8fd21d28 Fix for https://github.com/fish-shell/fish-shell/issues/209
Don't hang if stdin is closed and we have no bindings
2012-07-06 16:25:59 -07:00
maxfl
232ed91ec7 Fix read default prompt. It's now single line. 2012-07-06 15:31:45 -07:00
maxfl
7fccad156e Fix fish_prompt event
I've found that this modification fixes fish_prompt event
Fixes #164
2012-07-06 15:31:45 -07:00
ridiculousfish
11e56456a0 Fix for wrong sense for determining when an autoloaded function has changed
Addresses some of the complaints in https://github.com/fish-shell/fish-shell/pull/201
2012-07-06 15:30:41 -07:00
Emily Eisenberg
d07e78c59a Make prompt_pwd make a lot more sense
Remove the case statements, which were handled by the sed blocks anyway
Move around the '/' character in the regex, so only one regex is needed
Fix a bug where '/' is output as '//'
2012-07-06 14:46:50 -07:00
Sean DuBois
c45479a6e5 Created the man page for echo and test 2012-07-06 14:41:44 -07:00
ridiculousfish
345a528625 Untangle unescaping responsibilities in highlight.cpp. Fix cd autosuggestions to properly handle quotes. Factor out some of the quote unescaping behavior from reader.cpp to parse_util.cpp.
Add some autosuggestion tests
2012-07-06 14:34:53 -07:00
maxfl
01d8490255 Return the previous logic for '\\'.
The following expression now works:
```sh
switch '\\'
  case '\\'
  echo 1
end
```

Due to ambiguity, the following expression also works:
```sh
switch '\a'
  case '\\a'
  echo 1
end
```

By the way, the following expression now doesn't work, which was not the
case before, because of wrong escaping:
```sh
switch 'nn'
  case '\n'
  echo 1
end
```
2012-07-05 14:17:25 -07:00
maxfl
ea4b37d5c5 Fixed case behaviour
* case no properly handles -h and --help flags, i.e. treats it as
  pattern
* fixed case escaping:
The following expressions now work correctly:
switch '*'
  echo '*'
  echo Match any string
end
switch '*'
  echo '\*'
  echo Match asterix
end
switch '\\'
  echo '\\\\'
  echo Match slash
end

The same for '?' sign
2012-07-05 14:17:25 -07:00
Siteshwar Vashisht
a1319cb8aa Fixed crash caused by pressing pageup button when no match (for typed command) in history is found 2012-07-05 16:06:39 +05:30
ridiculousfish
35c49d62d7 Fix a class/struct disparity 2012-07-01 16:11:14 -07:00
ridiculousfish
0576a11a6f Added missing break in case R_SUPPRESS_AUTOSUGGESTION 2012-07-01 15:40:06 -07:00
Ian Munsie
c8f86d94c9 Add command to temporarily suppress the autosuggestion feature
Autosuggestion will be automatically re-enabled next time a character is
inserted. An alternative implementation would require another command to
explicitly re-enable it.

Signed-off-by: Ian Munsie <darkstarsword@gmail.com>
2012-07-01 15:37:10 -07:00
Ian Munsie
2b24eab26a Fix off by two in move_word left
This can be demonstrated with something like:

echo howdy hi<control-w>
echo howdy I<alt-b>

Previousely this would delete/move all the way to the start of 'howdy',
rather than just the word 'hi'/'I'.

It seems that the code to ignore the character under the cursor was
redundant, as all the cases I've tried with it removed seem to do the
right thing.

Signed-off-by: Ian Munsie <darkstarsword@gmail.com>
2012-07-01 15:37:10 -07:00
ridiculousfish
7bbc7a61ce Clarify the function name deferral in functions_def
Prohibit making a function with an empty name
2012-07-01 15:33:50 -07:00
maxfl
fe26284376 Doc is updated
btw. funced completion is updated
2012-07-01 14:20:43 -07:00
maxfl
ab62fe6496 Update funced
* Editor mode is no the default
* Use -i or --interactive or -e fish to edit function in interactive
  mode
* tmpname is now created with random number added and check that file
  do not already exist
* check $TMPDIR existence and put /tmp if it does not exist
* There is an undocumented feature to use functions, started with dash.
  Introduce necessary changes to funced, functions, def_function()
  in order to make it work properly.
* Delete editor guessing. Use $EDITOR variable or -e key
2012-07-01 14:20:43 -07:00
ridiculousfish
bc9bae0f7f Update make_tarball.sh to respect new branch name 2012-07-01 14:19:39 -07:00
maxfl
e5939d1a18 Disable default fish_pager_color_secondary
since it should be different, depending on terminal background
2012-06-29 18:53:22 -07:00
maxfl
5fadb7f200 fix logic 2012-06-29 18:53:22 -07:00
maxfl
a02aa7a316 Step coloring for fish_pager introduces new variable
fish_pager_color_secondary to set background of every second completion
line. It simplifies finding the options corresponding to given
description. Default color is 151515.
2012-06-29 18:53:22 -07:00
ridiculousfish
3ab26a5d40 Updated funced documentation to reflect new behavior 2012-06-29 18:41:37 -07:00
ridiculousfish
a8bae31939 Change funced script to default to using $EDITOR if set, and to allow -e to specify a particular editor (with fish meaning the builtin editor) 2012-06-29 18:35:26 -07:00
maxfl
faea588fb8 Support editing functions starting with dot 2012-06-29 16:42:34 -07:00
maxfl
792e246262 Add emacs to 'funced -e'
The order is also changed.
2012-06-29 16:42:34 -07:00
maxfl
85f19f9b8c Add -e option to funced
New option -e or --editor allows to edit function inside external
editor, rather than in reader.
2012-06-29 16:42:34 -07:00
ridiculousfish
40e4f49dbe Disable process expansion in autosuggestions
Addresses https://github.com/fish-shell/fish-shell/issues/39
2012-06-29 16:40:54 -07:00
Siteshwar Vashisht
05c721bebb Some improvements to validate commands suggested from history 2012-06-29 00:24:37 +05:30
maxfl
e995cc9329 fix #177 (complete 'cd' only with paths) 2012-06-26 19:17:32 -07:00
ridiculousfish
9457c115be Merge pull request #181 from kballard/fish_git_switch
Remove __fish_git_prompt's switch bug workaround
2012-06-26 13:00:31 -07:00
Kevin Ballard
1eddc79a90 Remove __fish_git_prompt's switch bug workaround
With the switch bug fixed, __fish_git_prompt can be very slightly
simplified by not recording the exit status of every case in the
describe style switch individually.
2012-06-25 15:19:47 -07:00
Siteshwar Vashisht
ccfc909eb2 Fixed crash caused by pressing pageup button 2012-06-25 03:02:37 +05:30
Kevin Ballard
1a93cbba1e Bring prompt_pwd under Darwin to parity with non-Darwin
The echo command to print the last path segment got a couplel of fixes,
but these fixes were only applied to the non-Darwin version. Copy these
fixes over to the Darwin version. Notably, this makes `/` stop
displaying as `//`.
2012-06-24 14:04:43 -07:00
Kevin Ballard
529d410bec Fix prompt_cwd on darwin systems when $PWD is "/private"
When $PWD is "/private", the prompt was printing "rivate". Skip the
"/private" stripping if there's nothing after it.

Prevent "/private" from being stripped anywhere but the beginning of the
PWD.

Fixes #173
2012-06-24 14:04:43 -07:00
Kevin Ballard
4d1c0eaa52 Fix var set in __fish_config_interactive.fish
__fish_config_interactive.fish uses the presence of __fish_init_1_50_0
to decide whether it should perform a one-time initialization of
universal variables. Unfortunately, it sets the variable
__fish_init_1_23_0. Fix this to set __fish_init_1_50_0 instead.
2012-06-24 13:57:15 -07:00
Kevin Ballard
adfd3c1098 Don't blow away $status in switch statements
Tweak the switch and case builtins to not blow away $status
inappropriately. Fixes issue #161.
2012-06-24 13:43:44 -07:00
Kevin Ballard
f7d0c4b065 Add a test case for switch error code (issue #161) 2012-06-24 13:43:44 -07:00
Siteshwar Vashisht
5fb32f1e2d Fixed declaration of wrealpath() function on FreeBSD 2012-06-23 10:59:53 +05:30
ridiculousfish
f69489a4fc Mostly fix https://github.com/fish-shell/fish-shell/issues/72 2012-06-21 16:58:26 -07:00
ridiculousfish
d34ce26c4e Merge pull request #169 from kballard/git_prompt_docs
Tweak documentation for __fish_git_prompt
2012-06-21 16:34:12 -07:00
Kevin Ballard
3f7fe94009 Tweak documentation for __fish_git_prompt
Add mention of the __fish_git_prompt_color variable.
2012-06-21 11:14:01 -07:00
Kevin Ballard
ae593decfc Replace __fish_git_branch_prompt.fish with __fish_git_prompt.fish
__fish_git_prompt.fish is a complete port of the __git_ps1 function from
git-completion.bash, with the relevant configuration variables changed
and some extra configuration added (namely, control over individual
colors and the status indicator characters).
2012-06-21 11:03:15 -07:00
Emil Eriksson
eea62125a1 Fixes title update in tmux and screen
* Seems that writestr( L"\x1b];" ); doesn't work in tmux and
	  screen. As discussed in fish-shell/fish-shell#47 the 2 was
	  removed. This commit adds the zero (L"\x1b]0;").
	* Tested in screen,tmux,iTerm and xterm.
2012-06-21 10:30:12 -07:00
Emil Eriksson
96f36a63dc Enable window title update when TERM is screen-X
* Uses the same logic as when TERM is
	  xterm-X to enable window-title updates when
	  running in screen.
2012-06-21 10:30:12 -07:00
ridiculousfish
19e183f02f Squashed commit of the following:
commit 5b7659ec3d5e67b8dad8d3543d87a0169dc9a9e9
Merge: 57f3df3 22a4cd6
Author: ridiculousfish <corydoras@ridiculousfish.com>
Date:   Thu Jun 21 10:15:41 2012 -0700

    Merge branch 'master' of https://github.com/maxfl/fish-shell into maxfl-master-base

commit 22a4cd686f
Author: maxfl <gmaxfl@gmail.com>
Date:   Tue Jun 19 15:51:43 2012 +0400

    set now expands the variable size, if index is outside it

commit 9b0ffa8315
Author: maxfl <gmaxfl@gmail.com>
Date:   Mon Jun 18 21:30:44 2012 +0400

    fixes #78

commit 78387fb391
Merge: c0e6096 93dc7d4
Author: maxfl <gmaxfl@gmail.com>
Date:   Mon Jun 18 21:27:47 2012 +0400

    Merge remote-tracking branch 'fishfish/master'

commit c0e60963c1
Merge: 32a98e7 1bead8a
Author: maxfl <gmaxfl@gmail.com>
Date:   Mon Jun 18 10:29:42 2012 +0400

    Merge remote-tracking branch 'fishfish/master'

commit 32a98e799e
Merge: 6e71021 f2b5292
Author: maxfl <gmaxfl@gmail.com>
Date:   Sat Jun 16 18:42:07 2012 +0400

    Merge remote-tracking branch 'fishfish/master'

commit 6e710211bc
Author: maxfl <gmaxfl@gmail.com>
Date:   Thu Jun 14 11:01:13 2012 +0400

    revert fish_pager

commit 731a29f35b
Author: maxfl <gmaxfl@gmail.com>
Date:   Thu Jun 14 10:57:41 2012 +0400

    revert fish_pager.cpp

commit 72c1bfc7bf
Merge: ea74ffa 9b781c4
Author: maxfl <gmaxfl@gmail.com>
Date:   Wed Jun 13 17:54:11 2012 +0400

    Merge branch 'master' into maxfl_completions

commit ea74ffa086
Author: maxfl <gmaxfl@gmail.com>
Date:   Wed Jun 13 17:35:20 2012 +0400

    __fish_complete_command now can understand '--arg=option' tokens
    latexmk completion is updated

commit 45b667826f
Author: maxfl <gmaxfl@gmail.com>
Date:   Wed Jun 13 16:46:47 2012 +0400

    . completion

commit 1c9f8ffc9e
Author: maxfl <gmaxfl@gmail.com>
Date:   Wed Jun 13 16:46:13 2012 +0400

    a lot of new completions

commit 8224d9f984
Author: Maxim Gonchar <gonchar@myhost.localdomain>
Date:   Tue Jun 12 20:19:31 2012 +0400

    A lot of new completions.
    Some small updates and fixes of old functions and completions.

commit 234ed8f5da
Author: Maxim Gonchar <gonchar@myhost.localdomain>
Date:   Tue Jun 12 20:03:44 2012 +0400

    step-coloring initial
    set_color correction
2012-06-21 10:24:49 -07:00
Siteshwar Vashisht
57f3df3cab Fix for crash while editing multiple lines requested in issue #143 2012-06-21 21:05:14 +05:30
David Adam (zanchey)
08e78e63cf docs: fix percentage sign escape character
(Patch taken from Debian packaging.)
2012-06-18 14:01:22 -07:00
ridiculousfish
9228dffe5e Don't generate completions if we already have bespoke completions in the data directory
Fixes https://github.com/fish-shell/fish-shell/issues/148
Also fix some Python3 issues
2012-06-18 13:59:34 -07:00
Siteshwar Vashisht
93dc7d4cc1 Add support for querying variables with scope options as requested in issue #132 2012-06-18 22:52:33 +05:30
ridiculousfish
1bead8adf7 Fix to create_manpage_completions.py to flush after every line (so you see more progress) and to put the cursor at the beginning (so it doesn't jump around) 2012-06-17 15:19:55 -07:00
David Adam (zanchey)
6681f3bfec only touch user_doc if doxygen installed
If doxygen isn't installed, an empty file called user_doc will be created.
If doxygen is later installed, the documentation will not generate correctly.
2012-06-17 15:04:20 -07:00
David Adam (zanchey)
71f8960ef1 make clean should remove all generated documentation 2012-06-17 15:04:20 -07:00
Evan Jones
01780f19b1 Fix other usages of \n in sed replacements. 2012-06-17 15:01:04 -07:00
Evan Jones
1fa0c4d4d3 alias: Support seds that don't support \n in replacements.
This makes the alias command work on Mac OS X.
2012-06-17 15:01:04 -07:00
ridiculousfish
eebe126842 Fix for a busted format string 2012-06-17 14:49:45 -07:00
ridiculousfish
ebfa285122 Teach __fish_print_help.fish about the new man path 2012-06-17 14:46:24 -07:00
ridiculousfish
27212719dc Removed an errant printf 2012-06-17 13:20:31 -07:00
Siteshwar Vashisht
17567028da Fixed a bug in wsetlocale() 2012-06-17 11:25:21 +05:30
ridiculousfish
34fd8e0e00 Tweak fork guards to be more forgiving
Fixes https://github.com/fish-shell/fish-shell/issues/101
2012-06-16 21:25:33 -07:00
ridiculousfish
6cf42075fc Fix to check for case insensitive filesystems in is_potential_path
Addresses https://github.com/fish-shell/fish-shell/issues/119
2012-06-16 14:08:58 -07:00
ridiculousfish
afd8d2f9ba Fix for https://github.com/fish-shell/fish-shell/issues/135
Don't use std::map::insert when we need to overwrite values
2012-06-16 13:05:58 -07:00
ridiculousfish
1d54bff385 Disble futimes() calls in hopes of fixing https://github.com/fish-shell/fish-shell/issues/122
This should keep sudo from thinking that the tty has changed as part of its tty_tickets feature.
2012-06-16 12:30:20 -07:00
ridiculousfish
6dd0013a5d Fix for extra space in some completions
Addresses https://github.com/fish-shell/fish-shell/issues/60
2012-06-16 10:30:05 -07:00
ridiculousfish
f2b5292bcb Squashed commit of the following:
commit 33358874f1c275d8b08186e29f24a7889d2b5224
Author: maxfl <gmaxfl@gmail.com>
Date:   Thu Jun 14 11:01:13 2012 +0400

    revert fish_pager

commit cee1bc8a66ec3adc9573b76e1aca3131cd32db83
Author: maxfl <gmaxfl@gmail.com>
Date:   Thu Jun 14 10:57:41 2012 +0400

    revert fish_pager.cpp

commit 27f3bd39dd9903009503d20a59a9e2ba84add07a
Author: maxfl <gmaxfl@gmail.com>
Date:   Wed Jun 13 17:35:20 2012 +0400

    __fish_complete_command now can understand '--arg=option' tokens
    latexmk completion is updated

commit 97b53a4b53de9389675783f3e90f58215d936356
Author: maxfl <gmaxfl@gmail.com>
Date:   Wed Jun 13 16:46:47 2012 +0400

    . completion

commit d5b63b9963b0a02a71f564e7392171c5eab005cd
Author: maxfl <gmaxfl@gmail.com>
Date:   Wed Jun 13 16:46:13 2012 +0400

    a lot of new completions

commit ceab87d99425124aa010c64ed062e27202b850d2
Author: Maxim Gonchar <gonchar@myhost.localdomain>
Date:   Tue Jun 12 20:19:31 2012 +0400

    A lot of new completions.
    Some small updates and fixes of old functions and completions.

commit 950aecd570b51e1b9dc444cc651b282a220e8d94
Author: Maxim Gonchar <gonchar@myhost.localdomain>
Date:   Tue Jun 12 20:03:44 2012 +0400

    step-coloring initial
    set_color correction
2012-06-15 17:30:33 -07:00
Gour-Gadadhara Dasa
3a94f6e8b3 initial version of Bazaar completion (created automatically) 2012-06-15 17:18:15 -07:00
Peter Feigl
f2846a0b78 correcting path of man-pages to ./man/man1/ instead of ./man/
Change the path where man-pages are installed to
/usr/share/fish/man/man1 instead of /usr/share/fish/man, so that tools
like mandb can process it.
2012-06-15 16:31:35 -07:00
Tim Gray
dbde7033d8 Added completions for brew (Homebrew)
An OS X package manager.
http://mxcl.github.com/homebrew/
2012-06-15 16:29:26 -07:00
ridiculousfish
18f04adccb Support for importing fish 1.x's history and format, and also bash 2012-06-15 16:24:05 -07:00
ridiculousfish
1ed65b6bd7 Fixed token search (note: this didn't work properly even in the original fish!)
Fixes https://github.com/fish-shell/fish-shell/issues/61
2012-06-15 16:24:05 -07:00
Siteshwar Vashisht
9b781c4c06 Modified alias.fish to show help message when executed with 0 arguments 2012-06-13 00:29:05 +05:30
Siteshwar Vashisht
25f9105a97 Made history --help show history man page and history is now saved only once while deleting items 2012-06-12 14:20:07 +05:30
Siteshwar Vashisht
602109bd8d Some improvements in bash configuration importer script 2012-06-12 11:08:30 +05:30
ridiculousfish
631d27f7a8 Merge pull request #104 from Teggy/master
Please consider to pull: formatting glitch in doc_src/history.txt makes Doxygen stumble
2012-06-11 15:48:00 -07:00
Torsten Grust
aad27a7a68 Fixed closing <pre> tag
- Was an opening tag, should have been a closing tag
- Confused Doxygen 1.8.1.1 on my machine (OS X 10.7), resulting in the fish man pages not being installed at all
2012-06-11 13:46:47 +02:00
ridiculousfish
4ac01154d9 SIGXCPY should be SIGXCPU
Fixes https://github.com/fish-shell/fish-shell/issues/97
2012-06-10 03:36:02 -07:00
ridiculousfish
4e2c7c57d7 Add text to INSTALL describing how to go back to the old shell.
This was requested in https://github.com/ridiculousfish/fishfish/pull/92
2012-06-10 01:35:30 -07:00
Robin Deits
6e65cfcc9a fish_title now sets both tab and window titles in iTerm2 2012-06-10 00:50:00 -07:00
Carl Johan Crafoord
96a4b7eaa7 Check for libiconv_open if we can't find iconv_open 2012-06-10 00:33:01 -07:00
ridiculousfish
64afada7f0 Switched from using 'type' to 'functions --query' since it's a lot cheaper 2012-06-10 00:21:11 -07:00
adisbladis
9f563f4873 Introduced fish_user_keybindings 2012-06-10 00:21:11 -07:00
Siteshwar Vashisht
ca61d0ee8b Fixed crash in history builtin for inputs like : history --search -prefix "echo" 2012-06-10 01:08:19 +05:30
Siteshwar Vashisht
e96dabadf1 Add Rekonq in list of browsers to display help 2012-06-08 21:11:26 +05:30
Siteshwar Vashisht
b6601338c2 Made C-d delete characters in multiple lines. 2012-06-07 21:18:02 +05:30
Siteshwar Vashisht
8167e1e07e Add new line after printing job information with jobs builtin. 2012-06-07 21:07:34 +05:30
Siteshwar Vashisht
23ce927301 Fixed a bug in manpage generator. 2012-06-07 20:48:54 +05:30
Siteshwar Vashisht
c7941fc7b0 Use manpath instead of man --path to find man page paths. 2012-06-07 00:00:43 +05:30
Siteshwar Vashisht
1078ad9ae9 Fixed following bugs in history function:
1. history function without any argument now correctly shows user's command history.
2. history --save now saves user's command history.
2012-06-06 20:54:27 +05:30
adisbladis
c0085cbc61 Python3 fixes in import_bash_settings.py 2012-06-05 20:40:51 -07:00
Adam
a49d245b92 Python3 fixes for webconfig.py 2012-06-05 20:40:51 -07:00
Adam
0ce6829e4c Python3 fixes in make_completions.py 2012-06-05 20:40:51 -07:00
Adam
4e3acdcbdc Unicode error fix in manpage completion with python3 2012-06-05 15:26:11 -07:00
Adam
85f808130d Manpage generation now works with python3 2012-06-05 15:26:11 -07:00
347 changed files with 15829 additions and 15428 deletions

3
.gitignore vendored
View File

@@ -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

View File

@@ -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
=======================

View File

@@ -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
View File

@@ -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
View 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.

View File

@@ -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 */

View File

@@ -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 );
};

View File

@@ -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

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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,

View File

@@ -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;

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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],

View File

@@ -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

View File

@@ -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
View 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

View File

@@ -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:

View 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>

View File

@@ -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.

View File

@@ -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>

View File

@@ -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>.

View File

@@ -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
View 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.

View File

@@ -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

View File

@@ -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
View 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
View File

@@ -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
View File

@@ -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 &current();
// 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

View File

@@ -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;

View File

@@ -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
*/

View File

@@ -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)
{

View File

@@ -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

View File

@@ -1,7 +1,6 @@
#
# Init file for fish
#
# @configure_input@
#
# Some things should only be done for login terminals

View File

@@ -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 );

View File

@@ -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
View File

@@ -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
View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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

View File

@@ -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

View File

@@ -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
View File

@@ -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();

View File

@@ -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

View File

@@ -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');
}

View File

@@ -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);

View File

@@ -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
View File

@@ -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:

View File

@@ -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;

View File

@@ -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.

View File

@@ -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, &quote, 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);

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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
View File

@@ -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;
}

View File

@@ -49,6 +49,8 @@ enum
R_REPAINT,
R_UP_LINE,
R_DOWN_LINE,
R_SUPPRESS_AUTOSUGGESTION,
R_ACCEPT_AUTOSUGGESTION
}
;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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
View File

@@ -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
View File

@@ -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

View File

@@ -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));
}
}

View File

@@ -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;

View File

@@ -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

View File

@@ -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
View 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
View 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

Binary file not shown.

98
osx/osx_fish_launcher.m Normal file
View 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;
}

View File

@@ -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++ )
{

View File

@@ -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, &quote, 1);
}
}
return result;
}

View File

@@ -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

File diff suppressed because it is too large Load Diff

284
parser.h
View File

@@ -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();

View File

@@ -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
View File

@@ -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
View File

@@ -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,

View File

@@ -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;
}
}
}

View File

@@ -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
View File

@@ -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
View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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.

View File

@@ -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

View File

@@ -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();

File diff suppressed because it is too large Load Diff

140
screen.h
View File

@@ -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

View File

@@ -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
View File

@@ -0,0 +1 @@
complete -c . -x -a "(__fish_complete_suffix .fish)"

View 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'

View File

@@ -0,0 +1 @@
__fish_complete_atool acat

View File

@@ -0,0 +1,3 @@
__fish_complete_lpr accept
complete -c accept -s r -d 'Accept reason' -x

View File

@@ -0,0 +1 @@
__fish_complete_atool adiff

Some files were not shown because too many files have changed in this diff Show More