From b92a09d5e7e3334312265cc718c2d4d4dc687bb7 Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Thu, 28 Aug 2014 13:19:38 -0700 Subject: [PATCH 01/10] Fix the assertion failure in expand_variables() expand_variables() is slightly confused about how to handle last_idx. On input, it expects it to be the index to start processing at, but when called recursively it always passes the current index. This means that it may sometimes pass an index 1 past the end of the input string. Notably, that happens when typing something like > echo "$foo (where "foo" is any string that is not a prefix of some existing variable name) Fix this by explicitly defining last_idx as being the last processed index, meaning the next index to process is actually last_idx-1. This means we should call it with next.size() instead of next.size()-1. --- expand.cpp | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/expand.cpp b/expand.cpp index c3cb95031..3851d6b2a 100644 --- a/expand.cpp +++ b/expand.cpp @@ -1051,22 +1051,27 @@ static size_t parse_slice(const wchar_t *in, wchar_t **end_ptr, std::vector &out, long last_idx, parse_error_list_t *errors) { - // We permit last_idx to be beyond the end of the string if and only if the string is empty - assert(instr.empty() || (last_idx >= 0 && (size_t)last_idx < instr.size())); - - // Make this explicit - if (instr.empty()) + const size_t insize = instr.size(); + + // last_idx may be 1 past the end of the string, but no further + assert(last_idx >= 0 && (size_t)last_idx <= insize); + + if (last_idx == 0) { append_completion(out, instr); return true; } - + bool is_ok = true; bool empty = false; - const size_t insize = instr.size(); wcstring var_tmp; @@ -1078,7 +1083,7 @@ static int expand_variables(parser_t &parser, const wcstring &instr, std::vector // CHECK( out, 0 ); - for (long i=last_idx; (i>=0) && is_ok && !empty; i--) + for (long i=last_idx-1; (i>=0) && is_ok && !empty; i--) { const wchar_t c = instr.at(i); if ((c == VARIABLE_EXPAND) || (c == VARIABLE_EXPAND_SINGLE)) @@ -1809,7 +1814,7 @@ int expand_string(const wcstring &input, std::vector &output, expa } else { - if (!expand_variables(parser, next, *out, next.size() - 1, errors)) + if (!expand_variables(parser, next, *out, next.size(), errors)) { return EXPAND_ERROR; } From 7638a7d53170b32fe9968066a6bead192732349d Mon Sep 17 00:00:00 2001 From: Philipp Klose Date: Sun, 24 Aug 2014 17:32:55 +0200 Subject: [PATCH 02/10] Fix typo in German translation --- po/de.po | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/po/de.po b/po/de.po index 8c19ee368..04e38b243 100644 --- a/po/de.po +++ b/po/de.po @@ -11563,7 +11563,7 @@ msgstr "" #~ msgstr "Führende Leerzeichen ignorieren" #~ msgid "Consider only blanks and alphanumerics" -#~ msgstr "Nur Leerzeichen und alfanumerische Zeichen beachten" +#~ msgstr "Nur Leerzeichen und alphanumerische Zeichen beachten" #~ msgid "Compare general numeric value" #~ msgstr "Vergleiche generelle numerische Werte" From 71ab40e53690cd89fcb013362404ef7578a5a6fe Mon Sep 17 00:00:00 2001 From: Konrad Borowski Date: Fri, 29 Aug 2014 14:19:55 +0200 Subject: [PATCH 03/10] Add Fossil command completions. --- share/completions/fossil.fish | 427 ++++++++++++++++++++++++++++++++++ 1 file changed, 427 insertions(+) create mode 100644 share/completions/fossil.fish diff --git a/share/completions/fossil.fish b/share/completions/fossil.fish new file mode 100644 index 000000000..bc04d0ca6 --- /dev/null +++ b/share/completions/fossil.fish @@ -0,0 +1,427 @@ +# fish completion for fossil +# http://www.fossil-scm.org/ + +function __fish_fossil_needs_command + test (count (commandline -poc)) -eq 1 +end + +function __fish_fossil_command + set -l cmd (commandline -poc) + test (count $cmd) -gt 1 + and contains $cmd[2] $argv +end + +function __fish_fossil_subcommand + set -l cmd (commandline -poc) + test (count $cmd) -eq 2 + and test $argv[1] = $cmd[2] +end + +function __fish_fossil_subsubcommand + set -l cmd (commandline -poc) + test (count $cmd) -ge 3 + and test $argv[1] = $cmd[2] + and test $argv[2] = $cmd[3] +end + +function __fish_fossil_subsubcommand_only + set -l cmd (commandline -poc) + test (count $cmd) -eq 3 + and test $argv[1] = $cmd[2] + and test $argv[2] = $cmd[3] +end + +function __fish_fossil_subsubsubcommand_only + set -l cmd (commandline -poc) + test (count $cmd) -eq 4 + and test $argv[1] = $cmd[2] + and test $argv[2] = $cmd[3] + and test $argv[3] = $cmd[4] +end + +function __fish_fossil_modified + fossil changes --rel-paths | cut -c12- + fossil extra --rel-paths +end + +# add +complete -c fossil -n __fish_fossil_needs_command -a add -x -d 'Add files to checkout' +complete -c fossil -n '__fish_fossil_command add' -l case-sensitive -x -a 'yes no' -d 'Case insensitive file matching' +complete -c fossil -n '__fish_fossil_command add' -l dotfiles -d 'Include dotfiles' +complete -c fossil -n '__fish_fossil_command add' -l ignore -r -d 'Files to ignore' +complete -c fossil -n '__fish_fossil_command add' -l clean -r -d 'Files to ignore' +complete -c fossil -n '__fish_fossil_command add' -a '(__fish_fossil_modified)' -x -d 'File' + +# addremove +complete -c fossil -n __fish_fossil_needs_command -a addremove -f -d 'Remove and add files to checkout' +complete -c fossil -n '__fish_fossil_command addremove' -l case-sensitive -x -a 'yes no' -d 'Case insensitive file matching' +complete -c fossil -n '__fish_fossil_command addremove' -l dotfiles -d 'Include dotfiles' +complete -c fossil -n '__fish_fossil_command addremove' -l ignore -r -d 'Files to ignore' +complete -c fossil -n '__fish_fossil_command addremove' -l clean -r -d 'Files to ignore' +complete -c fossil -n '__fish_fossil_command addremove' -s n -l dry-run -d 'Display actions without running' + +# all +complete -c fossil -n __fish_fossil_needs_command -a all -f -d 'Check all repositories' +complete -c fossil -n '__fish_fossil_subcommand all' -a changes -f -d 'List changes' +complete -c fossil -n '__fish_fossil_subcommand all' -a ignore -d 'Ignore repository' +complete -c fossil -n '__fish_fossil_subsubcommand all ignore' -x -a '(fossil all ls)' -d 'Ignore repository' +complete -c fossil -n '__fish_fossil_subcommand all' -a 'list ls' -d 'Display locations' +complete -c fossil -n '__fish_fossil_subcommand all' -a pull -d 'Pull repositories' +complete -c fossil -n '__fish_fossil_subcommand all' -a push -d 'Push repositories' +complete -c fossil -n '__fish_fossil_subcommand all' -a rebuild -d 'Rebuild repositories' +complete -c fossil -n '__fish_fossil_subcommand all' -a sync -d 'Sync repositories' + +# annotate +complete -c fossil -n __fish_fossil_needs_command -a annotate -d 'Shows file modifications' +complete -c fossil -n '__fish_fossil_command annotate' -l filevers -d 'Show file versions' +complete -c fossil -n '__fish_fossil_command annotate' -s l -l log -d 'List all analyzed versions' +complete -c fossil -n '__fish_fossil_command annotate' -s n -l limit -x -d 'Limit analyzed versions' + +# bisect +complete -c fossil -n __fish_fossil_needs_command -a bisect -f -d 'Find regressions' +complete -c fossil -n '__fish_fossil_subcommand bisect' -a bad -x -d 'Identify version as not working' +complete -c fossil -n '__fish_fossil_subcommand bisect' -a good -x -d 'Identify version as working' +complete -c fossil -n '__fish_fossil_subcommand bisect' -a log -d 'Show log of bisects in test order' +complete -c fossil -n '__fish_fossil_subcommand bisect' -a chart -d 'Show log of bisects in check-in order' +complete -c fossil -n '__fish_fossil_subcommand bisect' -a next -d 'Skip version' +complete -c fossil -n '__fish_fossil_subcommand bisect' -a options -d 'Show bisect options' +complete -c fossil -n '__fish_fossil_subsubcommand_only bisect options' -x -a 'auto-next' -d 'Automatically run bisect next' +complete -c fossil -n '__fish_fossil_subsubcommand_only bisect options' -x -a 'direct-only' -d 'Follow only primary parent-child links' +complete -c fossil -n '__fish_fossil_subsubcommand_only bisect options' -x -a 'display' -d 'Command to show after bisect next' +complete -c fossil -n '__fish_fossil_subsubsubcommand_only bisect options auto-next' -x -a 'on off' -d 'Automatically run bisect next' +complete -c fossil -n '__fish_fossil_subsubsubcommand_only bisect options direct-only' -x -a 'on off' -d 'Follow only primary parent-child links' +complete -c fossil -n '__fish_fossil_subsubsubcommand_only bisect options display' -x -a chart -d 'Show log of bisects in check-in order' +complete -c fossil -n '__fish_fossil_subsubsubcommand_only bisect options display' -x -a log -d 'Show log of bisects in test order' +complete -c fossil -n '__fish_fossil_subsubsubcommand_only bisect options display' -x -a status -d 'List versions between bad and good' +complete -c fossil -n '__fish_fossil_subsubsubcommand_only bisect options display' -x -a none -d 'Don\'t show anything' +complete -c fossil -n '__fish_fossil_subcommand bisect' -a reset -d 'Reinitialize bisect' +complete -c fossil -n '__fish_fossil_subcommand bisect' -a 'vlist ls status' -d 'List versions between bad and good' +complete -c fossil -n '__fish_fossil_subsubcommand bisect vlist' -s a -l all -d 'Show all versions' +complete -c fossil -n '__fish_fossil_subsubcommand bisect ls' -s a -l all -d 'Show all versions' +complete -c fossil -n '__fish_fossil_subsubcommand bisect status' -s a -l all -d 'Show all versions' +complete -c fossil -n '__fish_fossil_subcommand bisect' -a undo -d 'Undo latest bad/good command.' + +# branch +complete -c fossil -n __fish_fossil_needs_command -a branch -f -d 'Create a new branch' +complete -c fossil -n '__fish_fossil_subcommand branch' -a new -x -d 'Create new branch' +complete -c fossil -n '__fish_fossil_subsubcommand branch new' -l private -d 'Make branch local' +complete -c fossil -n '__fish_fossil_subsubcommand branch new' -l bgcolor -x -d 'Set background color' +complete -c fossil -n '__fish_fossil_subsubcommand branch new' -l nosign -d 'Don\'t sign the branch with GPG' +complete -c fossil -n '__fish_fossil_subsubcommand branch new' -l date-override -x -d 'Override date' +complete -c fossil -n '__fish_fossil_subsubcommand branch new' -l user-override -x -d 'Override user' +complete -c fossil -n '__fish_fossil_subcommand branch' -a 'list ls' -x -d 'List branches' +complete -c fossil -n '__fish_fossil_subsubcommand branch list' -s a -l all -d 'Show all branches' +complete -c fossil -n '__fish_fossil_subsubcommand branch ls' -s a -l all -d 'Show all branches' +complete -c fossil -n '__fish_fossil_subsubcommand branch list' -s c -l closed -d 'Show closed branches' +complete -c fossil -n '__fish_fossil_subsubcommand branch ls' -s c -l closed -d 'Show closed branches' +complete -c fossil -n '__fish_fossil_command branch' -s R -l repository -r -d 'Run command on repository' + +# cat +complete -c fossil -n __fish_fossil_needs_command -a cat -d 'Print a file' +complete -c fossil -n '__fish_fossil_command cat' -s R -l repository -r -d 'Run command on repository' +complete -c fossil -n '__fish_fossil_command cat' -s r -x -a '(fossil tag list)' -d 'Print specific revision' + +# changes +complete -c fossil -n __fish_fossil_needs_command -a cat -d 'List local changes' +complete -c fossil -n '__fish_fossil_command changes' -l abs-paths -d 'Display absolute paths' +complete -c fossil -n '__fish_fossil_command changes' -l rel-paths -d 'Display relative paths' +complete -c fossil -n '__fish_fossil_command changes' -l sha1sum -d 'Verify file status using SHA1' +complete -c fossil -n '__fish_fossil_command changes' -l header -d 'Identify the repository if there are changes' +complete -c fossil -n '__fish_fossil_command changes' -s v -l verbose -d 'Say (none) if there are no changes' + +# clean +complete -c fossil -n __fish_fossil_needs_command -a clean -d 'Delete all extra files' +complete -c fossil -n '__fish_fossil_command clean' -l case-sensitive -x -a 'yes no' -d 'Case insensitive file matching' +complete -c fossil -n '__fish_fossil_command clean' -l dotfiles -d 'Include dotfiles' +complete -c fossil -n '__fish_fossil_command clean' -l ignore -r -d 'Files to ignore' +complete -c fossil -n '__fish_fossil_command clean' -l clean -r -d 'Files to clean without prompting' +complete -c fossil -n '__fish_fossil_command clean' -s f -l force -d 'Remove without prompting' +complete -c fossil -n '__fish_fossil_command clean' -s n -l dry-run -d 'Display actions without running' +complete -c fossil -n '__fish_fossil_command clean' -l temp -d 'Only remove Fossil temporary files' +complete -c fossil -n '__fish_fossil_command clean' -s v -l verbose -d 'Show removed files' + +# clone +complete -c fossil -n __fish_fossil_needs_command -a clone -d 'Clone repository' +complete -c fossil -n '__fish_fossil_command clone' -s A -l admin-user -x -d 'Make username an administrator' +complete -c fossil -n '__fish_fossil_command clone' -l private -d 'Clone private branches' +complete -c fossil -n '__fish_fossil_command clone' -l ssl-identity -r -d 'Use SSL identity' + +# commit +complete -c fossil -n __fish_fossil_needs_command -a 'ci commit' -d 'Create new revision' +complete -c fossil -n '__fish_fossil_command ci commit' -l allow-conflict -d 'Allow unresolved merge conflicts' +complete -c fossil -n '__fish_fossil_command ci commit' -l allow-empty -d 'Allow empty check-ins' +complete -c fossil -n '__fish_fossil_command ci commit' -l allow-fork -d 'Allow forking' +complete -c fossil -n '__fish_fossil_command ci commit' -l allow-older -d 'Allow commit older than ancestor' +complete -c fossil -n '__fish_fossil_command ci commit' -l baseline -d 'Use a baseline manifest' +complete -c fossil -n '__fish_fossil_command ci commit' -l bgcolor -x -d 'Apply color to check-in' +complete -c fossil -n '__fish_fossil_command ci commit' -l branch -x -d 'Check-in to new branch' +complete -c fossil -n '__fish_fossil_command ci commit' -l branchcolor -x -d 'Apply color to branch' +complete -c fossil -n '__fish_fossil_command ci commit' -l close -d 'Close the branch' +complete -c fossil -n '__fish_fossil_command ci commit' -l delta -d 'Use a delta manifest' +complete -c fossil -n '__fish_fossil_command ci commit' -s m -l comment -x -d 'Commit comment' +complete -c fossil -n '__fish_fossil_command ci commit' -s M -l message-file -r -d 'Read commit comment from a file' +complete -c fossil -n '__fish_fossil_command ci commit' -l mimetype -x -d 'Mimetype of commit comment' +complete -c fossil -n '__fish_fossil_command ci commit' -s n -l dry-run -d 'Display actions without running' +complete -c fossil -n '__fish_fossil_command ci commit' -l no-warnings -d 'Omit all warnings' +complete -c fossil -n '__fish_fossil_command ci commit' -l nosign -d 'Don\'t sign the branch with GPG' +complete -c fossil -n '__fish_fossil_command ci commit' -l private -d 'Don\'t sync the changes' +complete -c fossil -n '__fish_fossil_command ci commit' -l tag -x -d 'Assign a tag to the checkin' + +# diff +complete -c fossil -n __fish_fossil_needs_command -a 'diff gdiff' -d 'Show differences' +complete -c fossil -n '__fish_fossil_command diff gdiff' -l binary -r -d 'Binary files glob pattern' +complete -c fossil -n '__fish_fossil_command diff gdiff' -l branch -x -a '(fossil tag list)' -d 'Show diff of branch' +complete -c fossil -n '__fish_fossil_command diff gdiff' -l brief -d 'Show file names only' +complete -c fossil -n '__fish_fossil_command diff gdiff' -s c -l context -x -d 'Context lines' +complete -c fossil -n '__fish_fossil_command diff gdiff' -l diff-binary -a 'yes no' -x -d 'Include binary files' +complete -c fossil -n '__fish_fossil_command diff gdiff' -s r -l from -x -a '(fossil tag list)' -d 'Select revision to compare with' +complete -c fossil -n '__fish_fossil_command diff gdiff' -s i -l internal -d 'Use internal diff logic' +complete -c fossil -n '__fish_fossil_command diff gdiff' -s y -l side-by-side -d 'Side-by-side diff' +complete -c fossil -n '__fish_fossil_command diff gdiff' -l tk -d 'Launch GUI' +complete -c fossil -n '__fish_fossil_command diff gdiff' -l to -x -a '(fossil tag list)' -d 'Select revision to compare to' +complete -c fossil -n '__fish_fossil_command diff gdiff' -l unified -d 'Unified diff' +complete -c fossil -n '__fish_fossil_command diff gdiff' -s v -l verbose -d 'Output complete text' +complete -c fossil -n '__fish_fossil_command diff gdiff' -s W -l width -x -d 'Line width in side-by-side diff' + +# export +complete -c fossil -n __fish_fossil_needs_command -a export -d 'Export repository to git' +complete -c fossil -n '__fish_fossil_command export' -l export-marks -r -d 'Export rids of exported data to file' +complete -c fossil -n '__fish_fossil_command export' -l import-marks -r -d 'Read rids of data to ignore from file' +complete -c fossil -n '__fish_fossil_command export' -s R -l repository -r -d 'Run command on repository' + +# extras +complete -c fossil -n __fish_fossil_needs_command -a extras -d 'Show files that aren\'t part of checkout' +complete -c fossil -n '__fish_fossil_command extras' -l abs-paths -d 'Display absolute paths' +complete -c fossil -n '__fish_fossil_command extras' -l case-sensitive -x -a 'yes no' -d 'Case insensitive file matching' +complete -c fossil -n '__fish_fossil_command extras' -l dotfiles -d 'Include dotfiles' +complete -c fossil -n '__fish_fossil_command extras' -l ignore -r -d 'Files to ignore' +complete -c fossil -n '__fish_fossil_command extras' -l rel-paths -d 'Display relative paths' + +# finfo +complete -c fossil -n __fish_fossil_needs_command -a finfo -d 'Print complete file history' +complete -c fossil -n '__fish_fossil_command finfo' -s b -l brief -d 'Display one-line summary' +complete -c fossil -n '__fish_fossil_command finfo' -l case-sensitive -x -a 'yes no' -d 'Case insensitive file matching' +complete -c fossil -n '__fish_fossil_command finfo' -s l -l log -d 'Select log mode (default)' +complete -c fossil -n '__fish_fossil_command finfo' -s n -l limit -x -d 'Limit analyzed versions' +complete -c fossil -n '__fish_fossil_command finfo' -l offset -x -d 'Skip changes' +complete -c fossil -n '__fish_fossil_command finfo' -s p -l print -d 'Select print mode' +complete -c fossil -n '__fish_fossil_command finfo' -s r -l revision -x -a '(fossil tag list)' -d 'Print specific revision' +complete -c fossil -n '__fish_fossil_command finfo' -s s -l status -d 'Select status mode' + +# help +complete -c fossil -n __fish_fossil_needs_command -a help -d 'Display help' +complete -c fossil -n '__fish_fossil_command help' -s a -l all -d 'Show main and auxiliary commands' +complete -c fossil -n '__fish_fossil_command help' -s t -l test -d 'Show test commands only' +complete -c fossil -n '__fish_fossil_command help' -s x -l aux -d 'Show auxilary commands only' +complete -c fossil -n '__fish_fossil_command help' -s w -l www -d 'Show list of web UI pages' + +# import +complete -c fossil -n __fish_fossil_needs_command -a import -d 'Import repository from git' +complete -c fossil -n '__fish_fossil_command import' -l incremental -d 'Allow importing into existing repository' + +# info +complete -c fossil -n __fish_fossil_needs_command -a import -d 'Provide information about object' +complete -c fossil -n '__fish_fossil_command import' -s R -l repository -r -d 'Run command on repository' +complete -c fossil -n '__fish_fossil_command import' -s v -l verbose -d 'Show extra information' + +# init +complete -c fossil -n __fish_fossil_needs_command -a 'init new' -d 'Create a repository' +complete -c fossil -n '__fish_fossil_command init new' -l template -r -d 'Copy settings from repository' +complete -c fossil -n '__fish_fossil_command init new' -s A -l admin-user -r -d 'Make username an administrator' +complete -c fossil -n '__fish_fossil_command init new' -l date-override -x -d 'Override date' + +# json +complete -c fossil -n __fish_fossil_needs_command -a json -d 'Make JSON request' +complete -c fossil -n '__fish_fossil_command json' -s R -l repository -r -d 'Run command on repository' +# TODO more + +# ls +complete -c fossil -n __fish_fossil_needs_command -a ls -d 'List files' +complete -c fossil -n '__fish_fossil_command ls' -a '(fossil tag list)' -d 'Tag' +complete -c fossil -n '__fish_fossil_command ls' -l age -d 'Show commit time' +complete -c fossil -n '__fish_fossil_command ls' -s v -l verbose -d 'Provide extra information' + +# merge +complete -c fossil -n __fish_fossil_needs_command -a merge -d 'Merge commits' +complete -c fossil -n '__fish_fossil_command merge' -a '(fossil tag list)' -d 'Tag' +complete -c fossil -n '__fish_fossil_command merge' -l baseline -a '(fossil tag list)' -x -d 'Use baseline' +complete -c fossil -n '__fish_fossil_command merge' -l binary -r -d 'Binary files glob pattern' +complete -c fossil -n '__fish_fossil_command merge' -l case-sensitive -x -a 'yes no' -d 'Case insensitive file matching' +complete -c fossil -n '__fish_fossil_command merge' -s f -l force -d 'Allow empty merge' +complete -c fossil -n '__fish_fossil_command merge' -l integrate -d 'Close merged branch' +complete -c fossil -n '__fish_fossil_command merge' -s n -l dry-run -d 'Display actions without running' +complete -c fossil -n '__fish_fossil_command merge' -s v -l verbose -d 'Show extra information' + +# mv +complete -c fossil -n __fish_fossil_needs_command -a mv -d 'Move file' +complete -c fossil -n '__fish_fossil_command mv' -l case-sensitive -x -a 'yes no' -d 'Case insensitive file matching' + +# open +complete -c fossil -n __fish_fossil_needs_command -a open -d 'Open repository' +complete -c fossil -n '__fish_fossil_command open' -l keep -d 'Only modify manifest' +complete -c fossil -n '__fish_fossil_command open' -l nested -d 'Allow opening inside an opened repository' + +# pull +complete -c fossil -n __fish_fossil_needs_command -a pull -d 'Pull from a repository' +complete -c fossil -n '__fish_fossil_command pull' -s R -l repository -r -d 'Run command on repository' +complete -c fossil -n '__fish_fossil_command pull' -l private -r -d 'Pull private branches' + +# push +complete -c fossil -n __fish_fossil_needs_command -a push -d 'Push into a repository' +complete -c fossil -n '__fish_fossil_command push' -s R -l repository -r -d 'Run command on repository' +complete -c fossil -n '__fish_fossil_command push' -l private -d 'Pull private branches' + +# rebuild +complete -c fossil -n __fish_fossil_needs_command -a rebuild -d 'Rebuild a repository' +complete -c fossil -n '__fish_fossil_command rebuild' -l cluster -d 'Compute clusters' +complete -c fossil -n '__fish_fossil_command rebuild' -l compress -d 'Compress database' +complete -c fossil -n '__fish_fossil_command rebuild' -l force -d 'Force rebuild even with errors' +complete -c fossil -n '__fish_fossil_command rebuild' -l noverify -d 'Skip BLOB table verification' +complete -c fossil -n '__fish_fossil_command rebuild' -l pagesize -x -a '512 1024 2048 4096 8192 16384 32768 65536' -d 'Set the database pagesize' +complete -c fossil -n '__fish_fossil_command rebuild' -l randomize -d 'Scan in random order' +complete -c fossil -n '__fish_fossil_command rebuild' -l vacuum -d 'Run VACUUM' +complete -c fossil -n '__fish_fossil_command rebuild' -l deanalyze -d 'Remove ANALYZE tables' +complete -c fossil -n '__fish_fossil_command rebuild' -l analyze -d 'Run ANALYZE' +complete -c fossil -n '__fish_fossil_command rebuild' -l wal -d 'Set Write-Ahead-Log journalling' +complete -c fossil -n '__fish_fossil_command rebuild' -l stats -d 'Show statistics' + +# remote-url +complete -c fossil -n __fish_fossil_needs_command -a remote-url -d 'Default server URL' + +# revert +complete -c fossil -n __fish_fossil_needs_command -a revert -d 'Revert a commit' +complete -c fossil -n '__fish_fossil_command revert' -a '(fossil tag list)' -d 'Tag' +complete -c fossil -n '__fish_fossil_command revert' -s r -x -a '(fossil tag list)' -d 'Revert back to given revision' + +# rm +complete -c fossil -n __fish_fossil_needs_command -a 'rm delete' -d 'Remove a file from repository' +complete -c fossil -n '__fish_fossil_command rm delete' -l case-sensitive -x -a 'yes no' -d 'Case insensitive file matching' + +# settings +complete -c fossil -n __fish_fossil_needs_command -a settings -f -d 'Manage settings' +complete -c fossil -n '__fish_fossil_subcommand settings' -x -a access-log -d 'Log accesses' +complete -c fossil -n '__fish_fossil_subcommand settings' -x -a allow-symlinks -d 'Allow symbolic links' +complete -c fossil -n '__fish_fossil_subcommand settings' -x -a auto-captcha -d 'Allow automatically filling CAPTCHA' +complete -c fossil -n '__fish_fossil_subcommand settings' -x -a auto-hyperlink -d 'Use JavaScript to enable hyperlinks' +complete -c fossil -n '__fish_fossil_subcommand settings' -x -a auto-shun -d 'Pull list of shunned references from server' +complete -c fossil -n '__fish_fossil_subcommand settings' -x -a autosync -d 'Automatically sync the repository' +complete -c fossil -n '__fish_fossil_subcommand settings' -x -a binary-glob -d 'Binary files glob pattern' +complete -c fossil -n '__fish_fossil_subcommand settings' -x -a case-sensitive -d 'Case insensitive file matching' +complete -c fossil -n '__fish_fossil_subcommand settings' -x -a clean-glob -d 'Files to clean without prompting' +complete -c fossil -n '__fish_fossil_subcommand settings' -x -a clearsign -d 'Sign commits using GPG' +complete -c fossil -n '__fish_fossil_subcommand settings' -x -a crnl-glob -d 'Non-standard line endings allowed glob pattern' +complete -c fossil -n '__fish_fossil_subcommand settings' -x -a default-perms -d 'Permissions given to new users' +complete -c fossil -n '__fish_fossil_subcommand settings' -x -a diff-binary -d 'Allow binary files to be diffed' +complete -c fossil -n '__fish_fossil_subcommand settings' -x -a diff-command -d 'External diff command' +complete -c fossil -n '__fish_fossil_subcommand settings' -x -a dont-push -d 'Disallow pushing to the repository' +complete -c fossil -n '__fish_fossil_subcommand settings' -x -a editor -d 'Text editor for writing check-in comments' +complete -c fossil -n '__fish_fossil_subcommand settings' -x -a empty-dirs -d 'List of empty directories' +complete -c fossil -n '__fish_fossil_subcommand settings' -x -a encoding-glob -d 'Non-UTF-8 files glob pattern' +complete -c fossil -n '__fish_fossil_subcommand settings' -x -a gdiff-command -d 'Command to use for graphical diff' +complete -c fossil -n '__fish_fossil_subcommand settings' -x -a gmerge-command -d 'Command to use for graphical merge' +complete -c fossil -n '__fish_fossil_subcommand settings' -x -a http-port -d 'HTTP port for fossil ui' +complete -c fossil -n '__fish_fossil_subcommand settings' -x -a https-login -d 'Send login credentials using HTTPS' +complete -c fossil -n '__fish_fossil_subcommand settings' -x -a ignore-glob -d 'Files to ignore glob pattern' +complete -c fossil -n '__fish_fossil_subcommand settings' -x -a keep-glob -d 'Files to keep when cleaning' +complete -c fossil -n '__fish_fossil_subcommand settings' -x -a localauth -d 'Require authentication for localhost logins' +complete -c fossil -n '__fish_fossil_subcommand settings' -x -a main-branch -d 'Primary branch' +complete -c fossil -n '__fish_fossil_subcommand settings' -x -a manifest -d 'Automatically create manifest files' +complete -c fossil -n '__fish_fossil_subcommand settings' -x -a max-upload -d 'HTTP request size limit' +complete -c fossil -n '__fish_fossil_subcommand settings' -x -a mtime-changes -d 'Use modification times' +complete -c fossil -n '__fish_fossil_subcommand settings' -x -a pgp-command -d 'PGP command' +complete -c fossil -n '__fish_fossil_subcommand settings' -x -a proxy -d 'HTTP proxy URL' +complete -c fossil -n '__fish_fossil_subcommand settings' -x -a relative-paths -d 'Report relative paths' +complete -c fossil -n '__fish_fossil_subcommand settings' -x -a repo-cksum -d 'Compute checksums over all files' +complete -c fossil -n '__fish_fossil_subcommand settings' -x -a self-register -d 'Allow users to register themselves' +complete -c fossil -n '__fish_fossil_subcommand settings' -x -a ssh-command -d 'Command to use for SSH protocol' +complete -c fossil -n '__fish_fossil_subcommand settings' -x -a ssl-ca-location -d 'Location of SSL CA root certificates' +complete -c fossil -n '__fish_fossil_subcommand settings' -x -a ssl-identity -d 'SSL private certificate path' +complete -c fossil -n '__fish_fossil_subcommand settings' -x -a tcl -d 'Allow Tcl scripting' +complete -c fossil -n '__fish_fossil_subcommand settings' -x -a tcl-setup -d 'Tcl initialization script' +complete -c fossil -n '__fish_fossil_subcommand settings' -x -a th1-setup -d 'TH1 initialization script' +complete -c fossil -n '__fish_fossil_subcommand settings' -x -a web-browser -d 'Web browser name' +complete -c fossil -n '__fish_fossil_command settings' -l global -d 'Set globally' + +# sqlite3 +complete -c fossil -n __fish_fossil_needs_command -a sqlite3 -f -d 'Run SQL commands' + +# stash +complete -c fossil -n __fish_fossil_needs_command -a stash -f -d 'Manage stashes' +complete -c fossil -n '__fish_fossil_subcommand stash' -x -a 'save snapshot' -d 'Save current changes' +complete -c fossil -n '__fish_fossil_subsubcommand stash save' -s m -l comment -x -d 'Stash comment' +complete -c fossil -n '__fish_fossil_subsubcommand stash snapshot' -s m -l comment -x -d 'Stash comment' +complete -c fossil -n '__fish_fossil_subcommand stash' -x -a 'list ls' -d 'List all stashes' +complete -c fossil -n '__fish_fossil_subsubcommand stash list' -s v -l verbose -d 'Show information about files' +complete -c fossil -n '__fish_fossil_subsubcommand stash ls' -s v -l verbose -d 'Show information about files' +complete -c fossil -n '__fish_fossil_subcommand stash' -x -a show -d 'Show stash contents' +complete -c fossil -n '__fish_fossil_subcommand stash' -x -a pop -d 'Pop last stash' +complete -c fossil -n '__fish_fossil_subcommand stash' -x -a apply -d 'Apply stash' +complete -c fossil -n '__fish_fossil_subcommand stash' -x -a goto -d 'Updates to stash state' +complete -c fossil -n '__fish_fossil_subcommand stash' -x -a 'drop rm' -d 'Forget about stash' +complete -c fossil -n '__fish_fossil_subsubcommand stash drop' -s a -l all -d 'Forget ALL stashes' +complete -c fossil -n '__fish_fossil_subsubcommand stash rm' -s a -l all -d 'Forget ALL stashes' +complete -c fossil -n '__fish_fossil_subcommand stash' -x -a goto -d 'Compare stash' + +# status +complete -c fossil -n __fish_fossil_needs_command -a status -f -d 'Show status' +complete -c fossil -n '__fish_fossil_command status' -l abs-paths -d 'Display absolute paths' +complete -c fossil -n '__fish_fossil_command status' -l rel-paths -d 'Display relative paths' +complete -c fossil -n '__fish_fossil_command status' -l sha1sum -d 'Verify file status using SHA1' + +# sync +complete -c fossil -n __fish_fossil_needs_command -a sync -d 'Sync with a repository' +complete -c fossil -n '__fish_fossil_command sync' -s R -l repository -r -d 'Run command on repository' +complete -c fossil -n '__fish_fossil_command sync' -l private -r -d 'Sync private branches' + +# tag +complete -c fossil -n __fish_fossil_needs_command -a tag -d 'Manage tags' +complete -c fossil -n '__fish_fossil_subcommand tag' -x -a add -d 'Add tag to check-in' +complete -c fossil -n '__fish_fossil_subsubcommand tag add' -l raw -d 'Add raw tag' +complete -c fossil -n '__fish_fossil_subsubcommand tag add' -l propagate -d 'Propagate tag' +complete -c fossil -n '__fish_fossil_subcommand tag' -x -a remove -d 'Remove tag from check-in' +complete -c fossil -n '__fish_fossil_subsubcommand tag remove' -a '(fossil tag list)' -d 'Tag' +complete -c fossil -n '__fish_fossil_subsubcommand tag remove' -l raw -d 'Remove raw tag' +complete -c fossil -n '__fish_fossil_subcommand tag' -x -a find -d 'Find tag' +complete -c fossil -n '__fish_fossil_subsubcommand tag find' -l raw -d 'Find raw tag' +complete -c fossil -n '__fish_fossil_subsubcommand tag find' -s t -l type -x -a 'ci e' -d 'Find tag type' +complete -c fossil -n '__fish_fossil_subsubcommand tag find' -s n -l limit -x -d 'Limit number of tags' +complete -c fossil -n '__fish_fossil_subsubcommand tag find' -a '(fossil tag list)' -d 'Tag' +complete -c fossil -n '__fish_fossil_subcommand tag' -x -a list -d 'List tags' +complete -c fossil -n '__fish_fossil_subsubcommand tag list' -l raw -d 'List raw tags' + +# timeline +complete -c fossil -n __fish_fossil_needs_command -a timeline -d 'Show timeline' +complete -c fossil -n '__fish_fossil_command timeline' -s n -l limit -x -d 'Limit timeline entries' +complete -c fossil -n '__fish_fossil_command timeline' -s t -l type -x -a 'ci e t w' -d 'Output only event type' +complete -c fossil -n '__fish_fossil_command timeline' -s v -l verbose -d 'Output list of files changed' + +# ui +complete -c fossil -n __fish_fossil_needs_command -a ui -d 'Open web UI' +complete -c fossil -n __fish_fossil_needs_command -a server -d 'Start web server' +complete -c fossil -n '__fish_fossil_command ui server' -l localauth -d 'Enable automatic login for localhost' +complete -c fossil -n '__fish_fossil_command server' -l localhost -d 'Only listen on localhost' +complete -c fossil -n '__fish_fossil_command ui server' -s P -l port -d 'Port to listen on' +complete -c fossil -n '__fish_fossil_command ui server' -l th-trace -d 'Trace TH1 execution' +complete -c fossil -n '__fish_fossil_command ui server' -l baseurl -d 'Use base URL' +complete -c fossil -n '__fish_fossil_command ui server' -l notfound -d 'Redirect' +complete -c fossil -n '__fish_fossil_command ui server' -l files -d 'Static files glob' +complete -c fossil -n '__fish_fossil_command ui server' -l scgi -d 'Use SCGI rather than HTTP' + +# undo +complete -c fossil -n __fish_fossil_needs_command -a undo -d 'Undo the changes' +complete -c fossil -n '__fish_fossil_command undo' -s n -l dry-run -d 'Display actions without running' + +# update +complete -c fossil -n __fish_fossil_needs_command -a update -d 'Update version' +complete -c fossil -n '__fish_fossil_command update' -l case-sensitive -x -a 'yes no' -d 'Case insensitive file matching' +complete -c fossil -n '__fish_fossil_command update' -l debug -d 'Print debug information' +complete -c fossil -n '__fish_fossil_command update' -l latest -d 'Update to latest version' +complete -c fossil -n '__fish_fossil_command update' -s n -l dry-run -d 'Display actions without running' +complete -c fossil -n '__fish_fossil_command update' -s v -l verbose -d 'Print information about all files' + +# version +complete -c fossil -n __fish_fossil_needs_command -a version -d 'Print fossil version' +complete -c fossil -n '__fish_fossil_command version' -s v -l verbose -d 'Print version of optional features' + +# --help +complete -c fossil -l help -d 'Print help' From 130619d6b07c691b1f34c56120020adaff09c49a Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Fri, 22 Aug 2014 18:05:28 -0700 Subject: [PATCH 04/10] Fix $SHLVL Due to being read-only, SHLVL wasn't being incremented properly for recursive invocations of fish. --- env.cpp | 3 ++- tests/test3.in | 4 ++++ tests/test3.out | 2 ++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/env.cpp b/env.cpp index 91bc7ac80..353085b8b 100644 --- a/env.cpp +++ b/env.cpp @@ -443,7 +443,7 @@ void env_init(const struct config_paths_t *paths /* or NULL */) L"LINES", L"COLUMNS", L"PWD", - L"SHLVL", + //L"SHLVL", // will be inserted a bit lower down L"FISH_VERSION", }; for (size_t i=0; i < sizeof ro_keys / sizeof *ro_keys; i++) @@ -549,6 +549,7 @@ void env_init(const struct config_paths_t *paths /* or NULL */) } } env_set(L"SHLVL", nshlvl_str.c_str(), ENV_GLOBAL | ENV_EXPORT); + env_read_only.insert(L"SHLVL"); /* Set up the HOME variable */ if (env_get_string(L"HOME").missing_or_empty()) diff --git a/tests/test3.in b/tests/test3.in index 1bbacc0e3..56cceeb57 100644 --- a/tests/test3.in +++ b/tests/test3.in @@ -233,4 +233,8 @@ echo $testu echo Missing: $testu ../fish -c 'echo Missing: $testu' +# test SHLVL +# use a subshell to ensure a clean slate +env SHLVL= ../fish -c 'echo SHLVL: $SHLVL; ../fish -c \'echo SHLVL: $SHLVL\'' + true diff --git a/tests/test3.out b/tests/test3.out index d6cbdb9c8..50563c05b 100644 --- a/tests/test3.out +++ b/tests/test3.out @@ -21,3 +21,5 @@ Testing Universal Startup 2 Missing: Missing: +SHLVL: 1 +SHLVL: 2 From 24ac7d2698c06792cc78a82bb06f41fcdeb0934c Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Fri, 22 Aug 2014 14:02:29 -0700 Subject: [PATCH 05/10] Fix `commandline` behavior in bind functions When a key is bound to a fish function, if that function invokes `commandline`, it gets a stale copy of the commandline. This is because any keys passed to `self-insert` (the default) don't actually get added to the commandline until a special character is processed, such as the R_NULL that gets returned after running a binding for a fish command. To fix this, don't allow fish commands to be run for bindings if we're processing more than one key. When a key wants to invoke a fish command, instead we push the invocation sequence back onto the input, followed by an R_NULL, and return. This causes the input loop to break out and update the commandline. When it starts up again, it will re-process the keys and invoke the fish command. This is primarily an issue with pasting text that includes bound keys in it. Typed text is slow enough that fish will update the commandline between each character. --- I don't know of any way to write a test for this, but the issue can be reproduced as follows: > bind _ 'commandline -i _' This binds _ to a command that inserts _. Typing the following works: > echo wat_is_it But if you copy that line and paste it instead of typing it, the end result looks like > _echo wat_isit With this fix in place, the pasted output correctly matches the typed output. --- input.cpp | 29 ++++++++++++++++++++++------- input.h | 6 +++++- reader.cpp | 5 ++++- 3 files changed, 31 insertions(+), 9 deletions(-) diff --git a/input.cpp b/input.cpp index 128fe0fb8..0d7a91be0 100644 --- a/input.cpp +++ b/input.cpp @@ -549,8 +549,10 @@ void input_function_push_args(int code) /** Perform the action of the specified binding + allow_commands controls whether fish commands should be executed, or should + be deferred until later. */ -static void input_mapping_execute(const input_mapping_t &m) +static void input_mapping_execute(const input_mapping_t &m, bool allow_commands) { /* By default input functions always succeed */ input_function_status = true; @@ -575,7 +577,7 @@ static void input_mapping_execute(const input_mapping_t &m) { input_unreadch(code); } - else + else if (allow_commands) { /* This key sequence is bound to a command, which @@ -588,6 +590,16 @@ static void input_mapping_execute(const input_mapping_t &m) input_unreadch(R_NULL); } + else + { + /* We don't want to run commands yet. Put the characters back and return R_NULL */ + for (wcstring::const_reverse_iterator it = m.seq.rbegin(), end = m.seq.rend(); it != end; ++it) + { + input_unreadch(*it); + } + input_unreadch(R_NULL); + return; /* skip the input_set_bind_mode */ + } } input_set_bind_mode(m.sets_mode.c_str()); @@ -644,7 +656,7 @@ void input_unreadch(wint_t ch) input_common_unreadch(ch); } -static void input_mapping_execute_matching_or_generic() +static void input_mapping_execute_matching_or_generic(bool allow_commands) { const input_mapping_t *generic = NULL; @@ -669,14 +681,14 @@ static void input_mapping_execute_matching_or_generic() } else if (input_mapping_is_match(m)) { - input_mapping_execute(m); + input_mapping_execute(m, allow_commands); return; } } if (generic) { - input_mapping_execute(*generic); + input_mapping_execute(*generic, allow_commands); } else { @@ -687,7 +699,7 @@ static void input_mapping_execute_matching_or_generic() } } -wint_t input_readch() +wint_t input_readch(bool allow_commands) { CHECK_BLOCK(R_NULL); @@ -738,7 +750,10 @@ wint_t input_readch() else { input_unreadch(c); - input_mapping_execute_matching_or_generic(); + input_mapping_execute_matching_or_generic(allow_commands); + // regarding allow_commands, we're in a loop, but if a fish command + // is executed, R_NULL is unread, so the next pass through the loop + // we'll break out and return it. } } } diff --git a/input.h b/input.h index 1ab43c03f..18d0d2b63 100644 --- a/input.h +++ b/input.h @@ -103,8 +103,12 @@ void input_destroy(); readch attempts to parse it. If no more input follows after the escape key, it is assumed to be an actual escape key press, and is returned as such. + + The argument determines whether fish commands are allowed to be run + as bindings. If false, when a character is encountered that would + invoke a fish command, it is unread and R_NULL is returned. */ -wint_t input_readch(); +wint_t input_readch(bool allow_commands = true); /** Push a character or a readline function onto the stack of unread diff --git a/reader.cpp b/reader.cpp index bb3b57fc6..c45e62b23 100644 --- a/reader.cpp +++ b/reader.cpp @@ -3117,7 +3117,10 @@ const wchar_t *reader_readline(void) c = 0; break; } - c = input_readch(); + // only allow commands on the first key; otherwise, we might + // have data we need to insert on the commandline that the + // commmand might need to be able to see. + c = input_readch(i == 1); if ((!wchar_private(c)) && (c>31) && (c != 127)) { arr[i]=c; From cc52a59e1ac7155123a314067cc8ad9f3affe491 Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Sun, 24 Aug 2014 00:59:03 -0700 Subject: [PATCH 06/10] Rework how screen size is tracked The screen size is fetched after a SIGWINCH is delivered. The current implementation has two issues: * It calls ioctl() from the SIGWINCH signal handler, despite ioctl() not being a function that is known to be safe to call. * It's not thread-safe. Signals can be delivered on arbitrary threads, so we don't know if it's actually safe to be modifying the cached winsize in response to a signal. It's also plausible that the winsize may be requested from a background thread. To solve the first issue, we twiddle a volatile boolean flag in the signal handler and defer the ioctl() call until we actually request the screen size. To solve the second issue, we introduce a pthread rwlock around the cached winsize. A rwlock is used because it can be expected that there are likely to be far more window size reads than window size writes. If we were using C++11 we could probably get away with atomics, but since we don't have that (or boost), a rwlock should suffice. Fixes #1613. --- common.cpp | 125 +++++++++++++++++++++++++++++++++++++++++++++------ common.h | 50 +++++++++++++++++++-- function.cpp | 3 -- 3 files changed, 159 insertions(+), 19 deletions(-) diff --git a/common.cpp b/common.cpp index b8a2661d2..ad35470e5 100644 --- a/common.cpp +++ b/common.cpp @@ -90,9 +90,12 @@ const wchar_t *program_name; int debug_level=1; /** - This struct should be continually updated by signals as the term resizes, and as such always contain the correct current size. + This struct maintains the current state of the terminal size. It is updated on demand after receiving a SIGWINCH. + Do not touch this struct directly, it's managed with a rwlock. Use common_get_width()/common_get_height(). */ static struct winsize termsize; +static volatile bool termsize_valid; +static rwlock_t termsize_rwlock; static char *wcs2str_internal(const wchar_t *in, char *out); @@ -1686,26 +1689,44 @@ bool unescape_string(const wcstring &input, wcstring *output, unescape_flags_t e void common_handle_winch(int signal) { -#ifdef HAVE_WINSIZE - if (ioctl(1,TIOCGWINSZ,&termsize)!=0) - { - return; - } -#else - termsize.ws_col = 80; - termsize.ws_row = 24; + /* don't run ioctl() here, it's not safe to use in signals */ + termsize_valid = false; +} + +/* updates termsize as needed, and returns a copy of the winsize. */ +static struct winsize get_current_winsize() +{ +#ifndef HAVE_WINSIZE + struct winsize retval = {0}; + retval.ws_col = 80; + retval.ws_row = 24; + return retval; #endif + scoped_rwlock guard(termsize_rwlock, true); + struct winsize retval = termsize; + if (!termsize_valid) + { + struct winsize size; + if (ioctl(1,TIOCGWINSZ,&size) == 0) + { + retval = size; + guard.upgrade(); + termsize = retval; + } + termsize_valid = true; + } + return retval; } int common_get_width() { - return termsize.ws_col; + return get_current_winsize().ws_col; } int common_get_height() { - return termsize.ws_row; + return get_current_winsize().ws_row; } void tokenize_variable_array(const wcstring &val, std::vector &out) @@ -2223,7 +2244,7 @@ void scoped_lock::lock(void) { assert(! locked); assert(! is_forked_child()); - VOMIT_ON_FAILURE(pthread_mutex_lock(lock_obj)); + VOMIT_ON_FAILURE_NO_ERRNO(pthread_mutex_lock(lock_obj)); locked = true; } @@ -2231,7 +2252,7 @@ void scoped_lock::unlock(void) { assert(locked); assert(! is_forked_child()); - VOMIT_ON_FAILURE(pthread_mutex_unlock(lock_obj)); + VOMIT_ON_FAILURE_NO_ERRNO(pthread_mutex_unlock(lock_obj)); locked = false; } @@ -2250,6 +2271,84 @@ scoped_lock::~scoped_lock() if (locked) this->unlock(); } +void scoped_rwlock::lock(void) +{ + assert(! (locked || locked_shared)); + assert(! is_forked_child()); + VOMIT_ON_FAILURE_NO_ERRNO(pthread_rwlock_rdlock(rwlock_obj)); + locked = true; +} + +void scoped_rwlock::unlock(void) +{ + assert(locked); + assert(! is_forked_child()); + VOMIT_ON_FAILURE_NO_ERRNO(pthread_rwlock_unlock(rwlock_obj)); + locked = false; +} + +void scoped_rwlock::lock_shared(void) +{ + assert(! (locked || locked_shared)); + assert(! is_forked_child()); + VOMIT_ON_FAILURE_NO_ERRNO(pthread_rwlock_wrlock(rwlock_obj)); + locked_shared = true; +} + +void scoped_rwlock::unlock_shared(void) +{ + assert(locked_shared); + assert(! is_forked_child()); + VOMIT_ON_FAILURE_NO_ERRNO(pthread_rwlock_unlock(rwlock_obj)); + locked_shared = false; +} + +void scoped_rwlock::upgrade(void) +{ + assert(locked_shared); + assert(! is_forked_child()); + VOMIT_ON_FAILURE_NO_ERRNO(pthread_rwlock_unlock(rwlock_obj)); + locked = false; + VOMIT_ON_FAILURE_NO_ERRNO(pthread_rwlock_wrlock(rwlock_obj)); + locked_shared = true; +} + +scoped_rwlock::scoped_rwlock(pthread_rwlock_t &rwlock, bool shared) : rwlock_obj(&rwlock), locked(false), locked_shared(false) +{ + if (shared) + { + this->lock_shared(); + } + else + { + this->lock(); + } +} + +scoped_rwlock::scoped_rwlock(rwlock_t &rwlock, bool shared) : rwlock_obj(&rwlock.rwlock), locked(false), locked_shared(false) +{ + if (shared) + { + this->lock_shared(); + } + else + { + this->lock(); + } +} + +scoped_rwlock::~scoped_rwlock() +{ + if (locked) + { + this->unlock(); + } + else if (locked_shared) + { + this->unlock_shared(); + } +} + wcstokenizer::wcstokenizer(const wcstring &s, const wcstring &separator) : buffer(), str(), diff --git a/common.h b/common.h index ff12760d5..84c908aaa 100644 --- a/common.h +++ b/common.h @@ -121,7 +121,9 @@ inline bool selection_direction_is_cardinal(selection_direction_t dir) /** Helper macro for errors */ -#define VOMIT_ON_FAILURE(a) do { if (0 != (a)) { int err = errno; fprintf(stderr, "%s failed on line %d in file %s: %d (%s)\n", #a, __LINE__, __FILE__, err, strerror(err)); abort(); }} while (0) +#define VOMIT_ON_FAILURE(a) do { if (0 != (a)) { VOMIT_ABORT(errno, #a); } } while (0) +#define VOMIT_ON_FAILURE_NO_ERRNO(a) do { int err = (a); if (0 != err) { VOMIT_ABORT(err, #a); } } while (0) +#define VOMIT_ABORT(err, str) do { int code = (err); fprintf(stderr, "%s failed on line %d in file %s: %d (%s)\n", str, __LINE__, __FILE__, code, strerror(code)); abort(); } while(0) /** Exits without invoking destructors (via _exit), useful for code after fork. */ void exit_without_destructors(int code) __attribute__((noreturn)); @@ -544,12 +546,12 @@ class mutex_lock_t pthread_mutex_t mutex; mutex_lock_t() { - pthread_mutex_init(&mutex, NULL); + VOMIT_ON_FAILURE_NO_ERRNO(pthread_mutex_init(&mutex, NULL)); } ~mutex_lock_t() { - pthread_mutex_destroy(&mutex); + VOMIT_ON_FAILURE_NO_ERRNO(pthread_mutex_destroy(&mutex)); } }; @@ -571,6 +573,48 @@ public: ~scoped_lock(); }; +class rwlock_t +{ +public: + pthread_rwlock_t rwlock; + rwlock_t() + { + VOMIT_ON_FAILURE_NO_ERRNO(pthread_rwlock_init(&rwlock, NULL)); + } + + ~rwlock_t() + { + VOMIT_ON_FAILURE_NO_ERRNO(pthread_rwlock_destroy(&rwlock)); + } +}; + +/* + Scoped lock class for rwlocks + */ +class scoped_rwlock +{ + pthread_rwlock_t *rwlock_obj; + bool locked; + bool locked_shared; + + /* No copying */ + scoped_rwlock &operator=(const scoped_lock &); + scoped_rwlock(const scoped_lock &); + +public: + void lock(void); + void unlock(void); + void lock_shared(void); + void unlock_shared(void); + /* + upgrade shared lock to exclusive. + equivalent to `lock.unlock_shared(); lock.lock();` + */ + void upgrade(void); + scoped_rwlock(pthread_rwlock_t &rwlock, bool shared = false); + scoped_rwlock(rwlock_t &rwlock, bool shared = false); + ~scoped_rwlock(); +}; /** A scoped manager to save the current value of some variable, and optionally diff --git a/function.cpp b/function.cpp index 5fd376974..f4ae2442e 100644 --- a/function.cpp +++ b/function.cpp @@ -67,9 +67,6 @@ void function_autoload_t::command_removed(const wcstring &cmd) function_remove_ignore_autoload(cmd); } -/* Helper macro for vomiting */ -#define VOMIT_ON_FAILURE(a) do { if (0 != (a)) { int err = errno; fprintf(stderr, "%s failed on line %d in file %s: %d (%s)\n", #a, __LINE__, __FILE__, err, strerror(err)); abort(); }} while (0) - /** Kludgy flag set by the load function in order to tell function_add that the function being defined is autoloaded. There should be a From 7fce9e2411b9e63d949ad519f1b6a36744d3df4c Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Thu, 28 Aug 2014 18:27:23 -0700 Subject: [PATCH 07/10] Trim trailing newline on cmdsubst when IFS='' When $IFS is empty, command substitution no longer splits on newlines. However we still want to trim off a single trailing newline, as most commands will emit a trailing newline and it makes it harder to work with their output. --- exec.cpp | 45 +++++++++++++++++++++++++++++---------------- tests/read.in | 5 +++++ tests/read.out | 9 +++++++++ 3 files changed, 43 insertions(+), 16 deletions(-) diff --git a/exec.cpp b/exec.cpp index dfd04231e..3b5506da6 100644 --- a/exec.cpp +++ b/exec.cpp @@ -1540,7 +1540,7 @@ static int exec_subshell_internal(const wcstring &cmd, wcstring_list_t *lst, boo ASSERT_IS_MAIN_THREAD(); int prev_subshell = is_subshell; const int prev_status = proc_get_last_status(); - char sep=0; + bool split_output=false; //fprintf(stderr, "subcmd %ls\n", cmd.c_str()); @@ -1548,7 +1548,7 @@ static int exec_subshell_internal(const wcstring &cmd, wcstring_list_t *lst, boo if (! ifs.missing_or_empty()) { - sep = '\n'; + split_output=true; } is_subshell=1; @@ -1580,23 +1580,36 @@ static int exec_subshell_internal(const wcstring &cmd, wcstring_list_t *lst, boo { const char *begin = io_buffer->out_buffer_ptr(); const char *end = begin + io_buffer->out_buffer_size(); - const char *cursor = begin; - while (cursor < end) + if (split_output) { - // Look for the next separator - const char *stop = (const char *)memchr(cursor, sep, end - cursor); - const bool hit_separator = (stop != NULL); - if (! hit_separator) + const char *cursor = begin; + while (cursor < end) { - // If it's not found, just use the end - stop = end; - } - // Stop now points at the first character we do not want to copy - const wcstring wc = str2wcstring(cursor, stop - cursor); - lst->push_back(wc); + // Look for the next separator + const char *stop = (const char *)memchr(cursor, '\n', end - cursor); + const bool hit_separator = (stop != NULL); + if (! hit_separator) + { + // If it's not found, just use the end + stop = end; + } + // Stop now points at the first character we do not want to copy + const wcstring wc = str2wcstring(cursor, stop - cursor); + lst->push_back(wc); - // If we hit a separator, skip over it; otherwise we're at the end - cursor = stop + (hit_separator ? 1 : 0); + // If we hit a separator, skip over it; otherwise we're at the end + cursor = stop + (hit_separator ? 1 : 0); + } + } + else + { + // we're not splitting output, but we still want to trim off a trailing newline + if (end != begin && end[-1] == '\n') + { + --end; + } + const wcstring wc = str2wcstring(begin, end - begin); + lst->push_back(wc); } } diff --git a/tests/read.in b/tests/read.in index 53f9873b6..f8787c5ce 100644 --- a/tests/read.in +++ b/tests/read.in @@ -7,6 +7,11 @@ set -l IFS \t count (echo one\ntwo) set -l IFS count (echo one\ntwo) +echo [(echo -n one\ntwo)] +count (echo one\ntwo\n) +echo [(echo -n one\ntwo\n)] +count (echo one\ntwo\n\n) +echo [(echo -n one\ntwo\n\n)] set -le IFS function print_vars --no-scope-shadowing diff --git a/tests/read.out b/tests/read.out index 6d90fc0e7..1098045dd 100644 --- a/tests/read.out +++ b/tests/read.out @@ -1,6 +1,15 @@ 2 2 1 +[one +two] +1 +[one +two] +1 +[one +two +] 1 'hello' 1 'there' 1 'hello there' From cb29350954fd3bb2fce0c7eeb23e2e1d26418985 Mon Sep 17 00:00:00 2001 From: Nikolai Aleksandrovich Pavlov Date: Sun, 24 Aug 2014 16:40:49 +0400 Subject: [PATCH 08/10] Fix fish_vi_mode.fish --- share/functions/fish_vi_mode.fish | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/share/functions/fish_vi_mode.fish b/share/functions/fish_vi_mode.fish index 46a2a27d6..e431d0e4a 100644 --- a/share/functions/fish_vi_mode.fish +++ b/share/functions/fish_vi_mode.fish @@ -1,5 +1,6 @@ -function fish_prompt - fish_vi_prompt +function fish_vi_mode + function fish_prompt + fish_vi_prompt + end + set -g fish_key_bindings fish_vi_key_bindings end -set fish_key_bindings fish_vi_key_bindings -#fish_vi_key_bindings From 0f330d7226fc53bee22ab1fa6600106dd10383f4 Mon Sep 17 00:00:00 2001 From: Christos Kontas Date: Sat, 30 Aug 2014 07:58:27 +0200 Subject: [PATCH 09/10] Fix small typo in documentation --- doc_src/design.hdr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc_src/design.hdr b/doc_src/design.hdr index b0b4635ff..2ea220ddb 100644 --- a/doc_src/design.hdr +++ b/doc_src/design.hdr @@ -123,7 +123,7 @@ all the common features by simply looking at the user interface and guessing what the different buttons, menus and other widgets do. The traditional way to discover features in commandline programs is through manual pages. This requires both that the user starts to use a -different program, and the she/he then remembers the new information +different program, and then she/he remembers the new information until the next time she/he uses the same program. Examples: From 1d0279eac5f37fd4ec453b0151274ab3300da817 Mon Sep 17 00:00:00 2001 From: Konrad Borowski Date: Sat, 30 Aug 2014 11:18:49 +0200 Subject: [PATCH 10/10] Fix F1 binding to work with multiple tokens. --- share/functions/fish_default_key_bindings.fish | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/functions/fish_default_key_bindings.fish b/share/functions/fish_default_key_bindings.fish index 62c995c27..d33f7b53e 100644 --- a/share/functions/fish_default_key_bindings.fish +++ b/share/functions/fish_default_key_bindings.fish @@ -112,7 +112,7 @@ function fish_default_key_bindings -d "Default (Emacs-like) key bindings for fis bind \ed kill-word # Allow reading manpages by pressing F1 - bind $argv -k f1 'man (basename (commandline -po; echo))[1] ^/dev/null; or echo -n \a' + bind $argv -k f1 'man (basename (commandline -po; echo)[1]) ^/dev/null; or echo -n \a' # This will make sure the output of the current command is paged using the less pager when you press Meta-p bind $argv \ep '__fish_paginate'