diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 000000000..77ec281dc --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,17 @@ +--- +Checks: 'clang-diagnostic-*,clang-analyzer-*,cert-*,performance-*,portability-*,modernize-use-auto,modernize-loop-convert,modernize-use-bool-literals,modernize-use-using,hicpp-uppercase-literal-suffix,readability-make-member-function-const,readability-redundant-string-init,readability-inconsistent-declaration-parameter-name,readability-redundant-access-specifiers' +WarningsAsErrors: '' +HeaderFilterRegex: '' +AnalyzeTemporaryDtors: false +FormatStyle: File +CheckOptions: + - key: cert-dcl16-c.NewSuffixes + value: 'L;LL;LU;LLU' + - key: cert-oop54-cpp.WarnOnlyIfThisHasSuspiciousField + value: '0' + - key: modernize-loop-convert.MinConfidence + value: 'risky' + - key: modernize-use-auto.RemoveStars + value: '1' +... + diff --git a/.github/workflows/lockthreads.yml b/.github/workflows/lockthreads.yml new file mode 100644 index 000000000..33784c01e --- /dev/null +++ b/.github/workflows/lockthreads.yml @@ -0,0 +1,16 @@ +name: 'Lock threads' + +on: + schedule: + - cron: '0 * * * *' + +jobs: + lock: + runs-on: ubuntu-latest + steps: + - uses: dessant/lock-threads@v2 + with: + github-token: ${{ github.token }} + issue-lock-inactive-days: '90' + pr-lock-inactive-days: '90' + issue-exclude-labels: 'question' diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 000000000..a2aa88176 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,48 @@ +name: C/C++ CI + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + ubuntu: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Install deps + run: | + sudo apt install expect gettext libncurses5-dev libpcre2-dev + - name: cmake + run: | + mkdir build && cd build + cmake .. + - name: make + run: | + make + - name: make test + run: | + make test + + # macos: + + # runs-on: macos-latest + + # steps: + # - uses: actions/checkout@v2 + # - name: Install deps + # run: | + # brew install pcre2 + # - name: cmake + # run: | + # mkdir build && cd build + # cmake .. + # - name: make + # run: | + # make + # - name: make test + # run: | + # make test diff --git a/CHANGELOG.md b/CHANGELOG.md index f11d2d32b..64c691652 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,9 @@ ## Notable improvements and fixes - `fish --no-execute` will no longer complain about unknown commands or non-matching wildcards, as these could be defined differently at runtime (especially for functions). #977 - `jobs --quiet PID` will no longer print 'no suitable job' if the job for PID does not exist (e.g. because it has finished). #6809 +- A variable `fish_kill_signal` will be set to the signal that terminated the last foreground job, or `0` if the job exited normally. +- On BSD systems, with the `-s` option, `fish_md5` does not use the given string, but `-s`. From now on the string is used. +- Control-C no longer kills background jobs for which job control is disabled, matching POSIX semantics (#6828). ### Syntax changes and new commands @@ -14,6 +17,7 @@ #### New or improved bindings - New readline commands `undo` (Ctrl+_) and `redo` (Alt-/) can be used to revert changes to the command line or the pager search field (#6570). +- New function `__fish_preview_current_file` (Alt+O) opens the current file at the cursor in a pager (#6838). #### Improved prompts - The default and example prompts print the correct exit status for commands prefixed with `not` (#6566). @@ -29,12 +33,17 @@ - `tcpdump` - `tig` - `windscribe` + - `zopfli`, and `zopflipng` + - `nmap`, `ncat` + - `nc`, `netcat`, `nc.openbsd`, `nc.traditional` ### Deprecations and removed features ### For distributors and developers - fish source tarballs are now distributed using the XZ compression method (#5460). - Allow finishing builds on OS X <10.13.6 (previously builds would fail at the `codesign` step) +- The pkg-config file now uses pkg-config variables +- The default values for the extra_completionsdir, extra_functionsdir and extra_confdir options now use the installation prefix instead of hardcoding `/usr/local` --- diff --git a/CMakeLists.txt b/CMakeLists.txt index 25caca58c..5f9774ab0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,6 +10,7 @@ endif() include(cmake/Mac.cmake) project(fish) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # We are C++11. set(CMAKE_CXX_STANDARD 11) @@ -217,11 +218,11 @@ include(cmake/MacApp.cmake) # CMake does not support the "iquote" flag - https://gitlab.kitware.com/cmake/cmake/issues/15491 set(LINT_ARGS "-D$, -D>" "-I$, -I>") add_custom_target(lint - COMMAND build_tools/lint.fish -- ${LINT_ARGS} + COMMAND build_tools/lint.fish -p ${CMAKE_BINARY_DIR} -- ${LINT_ARGS} WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" ) add_custom_target(lint-all - COMMAND build_tools/lint.fish --all -- ${LINT_ARGS} + COMMAND build_tools/lint.fish --all -p ${CMAKE_BINARY_DIR} -- ${LINT_ARGS} WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" ) diff --git a/build_tools/lint.fish b/build_tools/lint.fish index f194c4324..f2315cd9f 100755 --- a/build_tools/lint.fish +++ b/build_tools/lint.fish @@ -13,7 +13,7 @@ set all no set kernel_name (uname -s) set machine_type (uname -m) -argparse a/all -- $argv +argparse a/all p/project= -- $argv # We only want -D and -I options to be passed thru to cppcheck. for arg in $argv @@ -117,6 +117,14 @@ if set -q c_files[1] # output will expect those messages to be written to stdout. oclint $c_files -- $argv 2>&1 end + + if type -q clang-tidy; and set -q _flag_project + echo + echo ======================================== + echo Running clang-tidy + echo ======================================== + clang-tidy -p $_flag_project $c_files + end else echo echo 'WARNING: No C/C++ files to check' diff --git a/cmake/Install.cmake b/cmake/Install.cmake index 39a83e27a..67331f93f 100644 --- a/cmake/Install.cmake +++ b/cmake/Install.cmake @@ -24,16 +24,20 @@ set(configure_input "This file was generated from a corresponding .in file.\ DO NOT MANUALLY EDIT THIS FILE!") +set(rel_completionsdir "fish/vendor_completions.d") +set(rel_functionsdir "fish/vendor_functions.d") +set(rel_confdir "fish/vendor_conf.d") + set(extra_completionsdir - /usr/local/share/fish/vendor_completions.d + "${datadir}/${rel_completionsdir}" CACHE STRING "Path for extra completions") set(extra_functionsdir - /usr/local/share/fish/vendor_functions.d + "${datadir}/${rel_functionsdir}" CACHE STRING "Path for extra functions") set(extra_confdir - /usr/local/share/fish/vendor_conf.d + "${datadir}/${rel_confdir}" CACHE STRING "Path for extra configuration") # These are the man pages that go in system manpath; all manpages go in the fish-specific manpath. @@ -99,7 +103,7 @@ fish_create_dirs(${rel_datadir}/fish/vendor_completions.d ${rel_datadir}/fish/ve ${rel_datadir}/fish/vendor_conf.d) fish_try_create_dirs(${rel_datadir}/pkgconfig) -configure_file(fish.pc.in fish.pc.noversion) +configure_file(fish.pc.in fish.pc.noversion @ONLY) add_custom_command(OUTPUT fish.pc COMMAND sed '/Version/d' fish.pc.noversion > fish.pc diff --git a/debian/control b/debian/control index e6aa58e0b..1e7bb3255 100644 --- a/debian/control +++ b/debian/control @@ -25,7 +25,7 @@ Package: fish-common Architecture: all Multi-Arch: foreign Depends: ${misc:Depends} -Recommends: fish, python3 (>= 3.5) +Recommends: fish, python3 (>= 3.5), python3-distutils Suggests: xdg-utils Replaces: fish (<= 2.1.1.dfsg-2) Description: friendly interactive shell (architecture-independent files) diff --git a/doc_src/cmds/breakpoint.rst b/doc_src/cmds/breakpoint.rst index 6fe19088b..b81923d21 100644 --- a/doc_src/cmds/breakpoint.rst +++ b/doc_src/cmds/breakpoint.rst @@ -1,6 +1,6 @@ .. _cmd-breakpoint: -breakpoint - Launch debug mode +breakpoint - launch debug mode ============================== Synopsis diff --git a/doc_src/cmds/cd.rst b/doc_src/cmds/cd.rst index f6d4491a2..ccb4348d2 100644 --- a/doc_src/cmds/cd.rst +++ b/doc_src/cmds/cd.rst @@ -27,8 +27,6 @@ As a special case, ``cd .`` is equivalent to ``cd $PWD``, which is useful in cas Examples -------- - - :: cd @@ -37,8 +35,7 @@ Examples cd /usr/src/fish-shell # changes the working directory to /usr/src/fish-shell - See Also -------- -See also the :ref:`cdh ` command for changing to a recently visited directory. +Navigate directories using the :ref:`directory history ` or the :ref:`directory stack ` diff --git a/doc_src/cmds/cdh.rst b/doc_src/cmds/cdh.rst index 37d0325fc..8c9a4d7de 100644 --- a/doc_src/cmds/cdh.rst +++ b/doc_src/cmds/cdh.rst @@ -10,15 +10,16 @@ Synopsis cdh [ directory ] - Description ----------- -``cdh`` with no arguments presents a list of recently visited directories. You can then select one of the entries by letter or number. You can also press :kbd:`Tab` to use the completion pager to select an item from the list. If you give it a single argument it is equivalent to ``cd directory``. +``cdh`` with no arguments presents a list of :ref:`recently visited directories `. You can then select one of the entries by letter or number. You can also press :kbd:`Tab` to use the completion pager to select an item from the list. If you give it a single argument it is equivalent to ``cd directory``. Note that the ``cd`` command limits directory history to the 25 most recently visited directories. The history is stored in the ``$dirprev`` and ``$dirnext`` variables which this command manipulates. If you make those universal variables your ``cd`` history is shared among all fish instances. See Also -------- -See also the :ref:`prevd ` and :ref:`pushd ` commands which also work with the recent ``cd`` history and are provided for compatibility with other shells. +- the :ref:`dirh ` command to print the directory history +- the :ref:`prevd ` command to move backward +- the :ref:`nextd ` command to move forward diff --git a/doc_src/cmds/commandline.rst b/doc_src/cmds/commandline.rst index c1e82f198..3f058b426 100644 --- a/doc_src/cmds/commandline.rst +++ b/doc_src/cmds/commandline.rst @@ -83,7 +83,7 @@ The `echo $flounder >&` is the first process, `less` the second and `and echo $c `$flounder` is the current token. -More examples:: +More examples: :: diff --git a/doc_src/cmds/complete.rst b/doc_src/cmds/complete.rst index 929cc6f4c..0b81af23d 100644 --- a/doc_src/cmds/complete.rst +++ b/doc_src/cmds/complete.rst @@ -76,13 +76,13 @@ the fish manual. - ``-u`` and ``--unauthoritative`` no longer do anything and are silently ignored. -Command specific tab-completions in ``fish`` are based on the notion of options and arguments. An option is a parameter which begins with a hyphen, such as '``-h``', '``-help``' or '``--help``'. Arguments are parameters that do not begin with a hyphen. Fish recognizes three styles of options, the same styles as the GNU version of the getopt library. These styles are: +Command specific tab-completions in ``fish`` are based on the notion of options and arguments. An option is a parameter which begins with a hyphen, such as ``-h``, ``-help`` or ``--help``. Arguments are parameters that do not begin with a hyphen. Fish recognizes three styles of options, the same styles as the GNU version of the getopt library. These styles are: -- Short options, like '``-a``'. Short options are a single character long, are preceded by a single hyphen and may be grouped together (like '``-la``', which is equivalent to '``-l -a``'). Option arguments may be specified in the following parameter ('``-w 32``') or by appending the option with the value ('``-w32``'). +- Short options, like ``-a``. Short options are a single character long, are preceded by a single hyphen and may be grouped together (like ``-la``, which is equivalent to ``-l -a``). Option arguments may be specified in the following parameter (``-w 32``) or by appending the option with the value (``-w32``). -- Old style long options, like '``-Wall``'. Old style long options can be more than one character long, are preceded by a single hyphen and may not be grouped together. Option arguments are specified in the following parameter ('``-ao null``'). +- Old style long options, like ``-Wall``. Old style long options can be more than one character long, are preceded by a single hyphen and may not be grouped together. Option arguments are specified in the following parameter (``-ao null``). -- GNU style long options, like '``--colors``'. GNU style long options can be more than one character long, are preceded by two hyphens, and may not be grouped together. Option arguments may be specified in the following parameter ('``--quoting-style shell``') or by appending the option with a '``=``' and the value ('``--quoting-style=shell``'). GNU style long options may be abbreviated so long as the abbreviation is unique ('``--h``') is equivalent to '``--help``' if help is the only long option beginning with an 'h'). +- GNU style long options, like ``--colors``. GNU style long options can be more than one character long, are preceded by two hyphens, and may not be grouped together. Option arguments may be specified in the following parameter (``--quoting-style shell``) or by appending the option with a ``=`` and the value (``--quoting-style=shell``). GNU style long options may be abbreviated so long as the abbreviation is unique (``--h``) is equivalent to ``--help`` if help is the only long option beginning with an 'h'). The options for specifying command name and command path may be used multiple times to define the same completions for multiple commands. @@ -111,7 +111,7 @@ The short style option ``-o`` for the ``gcc`` command requires that a file follo complete -c gcc -s o -r -The short style option ``-d`` for the ``grep`` command requires that one of the strings '``read``', '``skip``' or '``recurse``' is used. This can be specified writing: +The short style option ``-d`` for the ``grep`` command requires that one of the strings ``read``, ``skip`` or ``recurse`` is used. This can be specified writing: diff --git a/doc_src/cmds/dirh.rst b/doc_src/cmds/dirh.rst index 449d24878..f60c43d6a 100644 --- a/doc_src/cmds/dirh.rst +++ b/doc_src/cmds/dirh.rst @@ -13,8 +13,15 @@ Synopsis Description ----------- -``dirh`` prints the current directory history. The current position in the history is highlighted using the color defined in the ``fish_color_history_current`` environment variable. +``dirh`` prints the current :ref:`directory history `. The current position in the history is highlighted using the color defined in the ``fish_color_history_current`` environment variable. ``dirh`` does not accept any parameters. -Note that the ``cd`` command limits directory history to the 25 most recently visited directories. The history is stored in the ``$dirprev`` and ``$dirnext`` variables. +Note that the :ref:`cd ` command limits directory history to the 25 most recently visited directories. The history is stored in the ``$dirprev`` and ``$dirnext`` variables. + +See Also +-------- + +- the :ref:`cdh ` command to display a prompt to quickly navigate the history +- the :ref:`prevd ` command to move backward +- the :ref:`nextd ` command to move forward diff --git a/doc_src/cmds/dirs.rst b/doc_src/cmds/dirs.rst index e67d94293..0b6541e29 100644 --- a/doc_src/cmds/dirs.rst +++ b/doc_src/cmds/dirs.rst @@ -14,8 +14,13 @@ Synopsis Description ----------- -``dirs`` prints the current directory stack, as created by :ref:`pushd ` and modified by :ref:`popd `. +``dirs`` prints the current :ref:`directory stack `, as created by :ref:`pushd ` and modified by :ref:`popd `. With "-c", it clears the directory stack instead. ``dirs`` does not accept any parameters. + +See Also +-------- + +- the :ref:`cdh ` command which provides a more intuitive way to navigate to recently visited directories. diff --git a/doc_src/cmds/emit.rst b/doc_src/cmds/emit.rst index 92be19482..7a8c13c75 100644 --- a/doc_src/cmds/emit.rst +++ b/doc_src/cmds/emit.rst @@ -1,6 +1,6 @@ .. _cmd-emit: -emit - Emit a generic event +emit - emit a generic event =========================== Synopsis diff --git a/doc_src/cmds/end.rst b/doc_src/cmds/end.rst index 829220787..697c437ee 100644 --- a/doc_src/cmds/end.rst +++ b/doc_src/cmds/end.rst @@ -1,7 +1,7 @@ .. _cmd-end: -end - end a block of commands. -============================== +end - end a block of commands +============================= Synopsis -------- diff --git a/doc_src/cmds/fish_key_reader.rst b/doc_src/cmds/fish_key_reader.rst index 5af63ce5b..1761fe437 100644 --- a/doc_src/cmds/fish_key_reader.rst +++ b/doc_src/cmds/fish_key_reader.rst @@ -21,10 +21,6 @@ The following options are available: - ``-c`` or ``--continuous`` begins a session where multiple key sequences can be inspected. By default the program exits after capturing a single key sequence. -- ``-d`` or ``--debug=CATEGORY_GLOB`` enables debug output and specifies a glob for matching debug categories (like ``fish -d``). Defaults to empty. - -- ``-D`` or ``--debug-stack-frames=DEBUG_LEVEL`` specify how many stack frames to display when debug messages are written. The default is zero. A value of 3 or 4 is usually sufficient to gain insight into how a given debug call was reached but you can specify a value up to 128. - - ``-h`` or ``--help`` prints usage information. - ``-v`` or ``--version`` prints fish_key_reader's version and exits. diff --git a/doc_src/cmds/fish_update_completions.rst b/doc_src/cmds/fish_update_completions.rst index be0b57f0b..3651aa0d1 100644 --- a/doc_src/cmds/fish_update_completions.rst +++ b/doc_src/cmds/fish_update_completions.rst @@ -1,6 +1,6 @@ .. _cmd-fish_update_completions: -fish_update_completions - Update completions using manual pages +fish_update_completions - update completions using manual pages =============================================================== Synopsis diff --git a/doc_src/cmds/for.rst b/doc_src/cmds/for.rst index 3bd65fced..3acc8e548 100644 --- a/doc_src/cmds/for.rst +++ b/doc_src/cmds/for.rst @@ -1,7 +1,7 @@ .. _cmd-for: -for - perform a set of commands multiple times. -=============================================== +for - perform a set of commands multiple times +============================================== Synopsis -------- diff --git a/doc_src/cmds/history.rst b/doc_src/cmds/history.rst index 938f8953d..c3edb6226 100644 --- a/doc_src/cmds/history.rst +++ b/doc_src/cmds/history.rst @@ -1,6 +1,6 @@ .. _cmd-history: -history - Show and manipulate command history +history - show and manipulate command history ============================================= Synopsis diff --git a/doc_src/cmds/math.rst b/doc_src/cmds/math.rst index a69036425..29ea9d41f 100644 --- a/doc_src/cmds/math.rst +++ b/doc_src/cmds/math.rst @@ -1,6 +1,6 @@ .. _cmd-math: -math - Perform mathematics calculations +math - perform mathematics calculations ======================================= Synopsis diff --git a/doc_src/cmds/nextd.rst b/doc_src/cmds/nextd.rst index f87b329ba..0018868a6 100644 --- a/doc_src/cmds/nextd.rst +++ b/doc_src/cmds/nextd.rst @@ -10,23 +10,18 @@ Synopsis nextd [ -l | --list ] [POS] - Description ----------- -``nextd`` moves forwards ``POS`` positions in the history of visited directories; if the end of the history has been hit, a warning is printed. +``nextd`` moves forwards ``POS`` positions in the :ref:`history of visited directories `; if the end of the history has been hit, a warning is printed. If the ``-l`` or ``--list`` flag is specified, the current directory history is also displayed. Note that the ``cd`` command limits directory history to the 25 most recently visited directories. The history is stored in the ``$dirprev`` and ``$dirnext`` variables which this command manipulates. -You may be interested in the :ref:`cdh ` command which provides a more intuitive way to navigate to recently visited directories. - Example ------- - - :: cd /usr/src @@ -41,3 +36,9 @@ Example nextd # Working directory is now /usr/src/fish-shell +See Also +-------- + +- the :ref:`cdh ` command to display a prompt to quickly navigate the history +- the :ref:`dirh ` command to print the directory history +- the :ref:`prevd ` command to move backward diff --git a/doc_src/cmds/popd.rst b/doc_src/cmds/popd.rst index a1e49cf4e..86511e29b 100644 --- a/doc_src/cmds/popd.rst +++ b/doc_src/cmds/popd.rst @@ -10,13 +10,10 @@ Synopsis popd - Description ----------- -``popd`` removes the top directory from the directory stack and changes the working directory to the new top directory. Use :ref:`pushd ` to add directories to the stack or :ref:`dirs ` to print it. - -You may be interested in the :ref:`cdh ` command which provides a more intuitive way to navigate to recently visited directories. +``popd`` removes the top directory from the :ref:`directory stack ` and changes the working directory to the new top directory. Use :ref:`pushd ` to add directories to the stack. Example ------- @@ -35,3 +32,8 @@ Example # Working directory is now /usr/src # Directory stack contains /usr/src +See Also +-------- + +- the :ref:`dirs ` command to print the directory stack +- the :ref:`cdh ` command which provides a more intuitive way to navigate to recently visited directories. diff --git a/doc_src/cmds/prevd.rst b/doc_src/cmds/prevd.rst index 20b83566e..3efb4f131 100644 --- a/doc_src/cmds/prevd.rst +++ b/doc_src/cmds/prevd.rst @@ -13,19 +13,15 @@ Synopsis Description ----------- -``prevd`` moves backwards ``POS`` positions in the history of visited directories; if the beginning of the history has been hit, a warning is printed. +``prevd`` moves backwards ``POS`` positions in the :ref:`history of visited directories `; if the beginning of the history has been hit, a warning is printed. If the ``-l`` or ``--list`` flag is specified, the current history is also displayed. Note that the ``cd`` command limits directory history to the 25 most recently visited directories. The history is stored in the ``$dirprev`` and ``$dirnext`` variables which this command manipulates. -You may be interested in the :ref:`cdh ` command which provides a more intuitive way to navigate to recently visited directories. - Example ------- - - :: cd /usr/src @@ -40,3 +36,9 @@ Example nextd # Working directory is now /usr/src/fish-shell +See Also +-------- + +- the :ref:`cdh ` command to display a prompt to quickly navigate the history +- the :ref:`dirh ` command to print the directory history +- the :ref:`nextd ` command to move forward diff --git a/doc_src/cmds/prompt_pwd.rst b/doc_src/cmds/prompt_pwd.rst index 973caf61f..71bb75737 100644 --- a/doc_src/cmds/prompt_pwd.rst +++ b/doc_src/cmds/prompt_pwd.rst @@ -1,6 +1,6 @@ .. _cmd-prompt_pwd: -prompt_pwd - Print pwd suitable for prompt +prompt_pwd - print pwd suitable for prompt ========================================== Synopsis diff --git a/doc_src/cmds/pushd.rst b/doc_src/cmds/pushd.rst index 25755b10a..58d89cb7f 100644 --- a/doc_src/cmds/pushd.rst +++ b/doc_src/cmds/pushd.rst @@ -13,7 +13,7 @@ Synopsis Description ----------- -The ``pushd`` function adds ``DIRECTORY`` to the top of the directory stack and makes it the current working directory. :ref:`popd ` will pop it off and return to the original directory. +The ``pushd`` function adds ``DIRECTORY`` to the top of the :ref:`directory stack ` and makes it the current working directory. :ref:`popd ` will pop it off and return to the original directory. Without arguments, it exchanges the top two directories in the stack. @@ -21,10 +21,6 @@ Without arguments, it exchanges the top two directories in the stack. ``pushd -NUMBER`` rotates clockwise i.e. top to bottom. -See also :ref:`dirs ` to print the stack and ``dirs -c`` to clear it. - -You may be interested in the :ref:`cdh ` command which provides a more intuitive way to navigate to recently visited directories. - Example ------- @@ -49,3 +45,9 @@ Example popd # Working directory is now /usr/src/fish-shell # Directory stack contains /usr/src/fish-shell /tmp + +See Also +-------- + +- the :ref:`dirs ` command to print the directory stack +- the :ref:`cdh ` command which provides a more intuitive way to navigate to recently visited directories. diff --git a/doc_src/cmds/pwd.rst b/doc_src/cmds/pwd.rst index d54516c89..bc155c206 100644 --- a/doc_src/cmds/pwd.rst +++ b/doc_src/cmds/pwd.rst @@ -21,3 +21,8 @@ The following options are available: - ``-L`` or ``--logical`` Output the logical working directory, without resolving symlinks (default behavior). - ``-P`` or ``--physical`` Output the physical working directory, with symlinks resolved. + +See Also +-------- + +Navigate directories using the :ref:`directory history ` or the :ref:`directory stack ` diff --git a/doc_src/cmds/realpath.rst b/doc_src/cmds/realpath.rst index 7a567804d..ed1c87670 100644 --- a/doc_src/cmds/realpath.rst +++ b/doc_src/cmds/realpath.rst @@ -1,6 +1,6 @@ .. _cmd-realpath: -realpath - Convert a path to an absolute path without symlinks +realpath - convert a path to an absolute path without symlinks ============================================================== Synopsis diff --git a/doc_src/cmds/set.rst b/doc_src/cmds/set.rst index 2988b5145..f4d6e123c 100644 --- a/doc_src/cmds/set.rst +++ b/doc_src/cmds/set.rst @@ -1,7 +1,7 @@ .. _cmd-set: -set - display and change shell variables. -========================================= +set - display and change shell variables +======================================== Synopsis -------- diff --git a/doc_src/cmds/source.rst b/doc_src/cmds/source.rst index b8ec6ed64..af2203992 100644 --- a/doc_src/cmds/source.rst +++ b/doc_src/cmds/source.rst @@ -1,7 +1,7 @@ .. _cmd-source: -source - evaluate contents of file. -=================================== +source - evaluate contents of file +================================== Synopsis -------- @@ -19,7 +19,7 @@ Description fish will search the working directory to resolve relative paths but will not search ``$PATH``. -If no file is specified and stdin is not the terminal, or if the file name '``-``' is used, stdin will be read. +If no file is specified and stdin is not the terminal, or if the file name ``-`` is used, stdin will be read. The exit status of ``source`` is the exit status of the last job to execute. If something goes wrong while opening or reading the file, ``source`` exits with a non-zero status. diff --git a/doc_src/cmds/status.rst b/doc_src/cmds/status.rst index 25fb27382..bed320da0 100644 --- a/doc_src/cmds/status.rst +++ b/doc_src/cmds/status.rst @@ -23,7 +23,7 @@ Synopsis status function status line-number status stack-trace - status job-control CONTROL-TYPE + status job-control CONTROL_TYPE status features status test-feature FEATURE @@ -63,7 +63,7 @@ The following operations (sub-commands) are available: - ``stack-trace`` prints a stack trace of all function calls on the call stack. Also ``print-stack-trace``, ``-t`` or ``--print-stack-trace``. -- ``job-control CONTROL-TYPE`` sets the job control type, which can be ``none``, ``full``, or ``interactive``. Also ``-j CONTROL-TYPE`` or ``--job-control=CONTROL-TYPE``. +- ``job-control CONTROL_TYPE`` sets the job control type, which can be ``none``, ``full``, or ``interactive``. Also ``-j CONTROL_TYPE`` or ``--job-control CONTROL_TYPE``. - ``features`` lists all available feature flags. diff --git a/doc_src/cmds/string-split.rst b/doc_src/cmds/string-split.rst index 17a979d0a..857d9fed0 100644 --- a/doc_src/cmds/string-split.rst +++ b/doc_src/cmds/string-split.rst @@ -8,8 +8,8 @@ Synopsis :: - string split [(-m | --max) MAX] [(-n | --no-empty)] [(-q | --quiet)] [(-r | --right)] SEP [STRING...] - string split0 [(-m | --max) MAX] [(-n | --no-empty)] [(-q | --quiet)] [(-r | --right)] [STRING...] + string split [(-f | --fields) FIELDS] [(-m | --max) MAX] [(-n | --no-empty)] [(-q | --quiet)] [(-r | --right)] SEP [STRING...] + string split0 [(-f | --fields) FIELDS] [(-m | --max) MAX] [(-n | --no-empty)] [(-q | --quiet)] [(-r | --right)] [STRING...] .. END SYNOPSIS @@ -20,6 +20,8 @@ Description ``string split`` splits each STRING on the separator SEP, which can be an empty string. If ``-m`` or ``--max`` is specified, at most MAX splits are done on each STRING. If ``-r`` or ``--right`` is given, splitting is performed right-to-left. This is useful in combination with ``-m`` or ``--max``. With ``-n`` or ``--no-empty``, empty results are excluded from consideration (e.g. ``hello\n\nworld`` would expand to two strings and not three). Exit status: 0 if at least one split was performed, or 1 otherwise. +Use ``-f`` or ``--fields`` to print out specific fields. Unless ``--allow-empty`` is used, if a given field does not exist, then the command exits with status 1 and does not print anything. + See also the ``--delimiter`` option of the :ref:`read ` command. ``string split0`` splits each STRING on the zero byte (NUL). Options are the same as ``string split`` except that no separator is given. @@ -49,6 +51,11 @@ Examples b c + >_ string split --allow-empty -f1,3,5 '' abc + a + c + + NUL Delimited Examples ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/doc_src/cmds/test.rst b/doc_src/cmds/test.rst index a1f406276..4871467a7 100644 --- a/doc_src/cmds/test.rst +++ b/doc_src/cmds/test.rst @@ -191,7 +191,7 @@ which is logically equivalent to the following: Standards --------- -``test`` implements a subset of the `IEEE Std 1003.1-2008 (POSIX.1) standard `__. The following exceptions apply: +``test`` implements a subset of the `IEEE Std 1003.1-2008 (POSIX.1) standard `__. The following exceptions apply: - The ``<`` and ``>`` operators for comparing strings are not implemented. diff --git a/doc_src/completions.rst b/doc_src/completions.rst index 302b8770f..b4624780d 100644 --- a/doc_src/completions.rst +++ b/doc_src/completions.rst @@ -7,7 +7,7 @@ To specify a completion, use the ``complete`` command. ``complete`` takes as a p To provide a list of possible completions for myprog, use the ``-a`` switch. If ``myprog`` accepts the arguments start and stop, this can be specified as ``complete -c myprog -a 'start stop'``. The argument to the ``-a`` switch is always a single string. At completion time, it will be tokenized on spaces and tabs, and variable expansion, command substitution and other forms of parameter expansion will take place. -``fish`` has a special syntax to support specifying switches accepted by a command. The switches ``-s``, ``-l`` and ``-o`` are used to specify a short switch (single character, such as ``-l``), a gnu style long switch (such as '``--color``') and an old-style long switch (like '``-shuffle``'), respectively. If the command 'myprog' has an option '-o' which can also be written as '``--output``', and which can take an additional value of either 'yes' or 'no', this can be specified by writing:: +``fish`` has a special syntax to support specifying switches accepted by a command. The switches ``-s``, ``-l`` and ``-o`` are used to specify a short switch (single character, such as ``-l``), a gnu style long switch (such as ``--color``) and an old-style long switch (like ``-shuffle``), respectively. If the command 'myprog' has an option '-o' which can also be written as ``--output``, and which can take an additional value of either 'yes' or 'no', this can be specified by writing:: complete -c myprog -s o -l output -a "yes no" @@ -71,7 +71,7 @@ For examples of how to write your own complex completions, study the completions Useful functions for writing completions ---------------------------------------- -``fish`` ships with several functions that are very useful when writing command specific completions. Most of these functions name begins with the string '``__fish_``'. Such functions are internal to ``fish`` and their name and interface may change in future fish versions. Still, some of them may be very useful when writing completions. A few of these functions are described here. Be aware that they may be removed or changed in future versions of fish. +``fish`` ships with several functions that are very useful when writing command specific completions. Most of these functions name begins with the string ``__fish_``. Such functions are internal to ``fish`` and their name and interface may change in future fish versions. Still, some of them may be very useful when writing completions. A few of these functions are described here. Be aware that they may be removed or changed in future versions of fish. Functions beginning with the string ``__fish_print_`` print a newline separated list of strings. For example, ``__fish_print_filesystems`` prints a list of all known file systems. Functions beginning with ``__fish_complete_`` print out a newline separated list of completions with descriptions. The description is separated from the completion by a tab character. @@ -100,7 +100,7 @@ Functions beginning with the string ``__fish_print_`` print a newline separated Where to put completions ------------------------ -Completions can be defined on the commandline or in a configuration file, but they can also be automatically loaded. Fish automatically searches through any directories in the list variable ``$fish_complete_path``, and any completions defined are automatically loaded when needed. A completion file must have a filename consisting of the name of the command to complete and the suffix '``.fish``'. +Completions can be defined on the commandline or in a configuration file, but they can also be automatically loaded. Fish automatically searches through any directories in the list variable ``$fish_complete_path``, and any completions defined are automatically loaded when needed. A completion file must have a filename consisting of the name of the command to complete and the suffix ``.fish``. By default, Fish searches the following for completions, using the first available file that it finds: diff --git a/doc_src/design.rst b/doc_src/design.rst index 362b8ffb8..73a36d7ab 100644 --- a/doc_src/design.rst +++ b/doc_src/design.rst @@ -31,7 +31,7 @@ Examples: - Having both aliases and functions is confusing, especially since both of them have limitations and problems. ``fish`` functions have none of the drawbacks of either syntax. -- The many Posix quoting styles are silly, especially $''. +- The many Posix quoting styles are silly, especially ``$``. The law of responsiveness diff --git a/doc_src/faq.rst b/doc_src/faq.rst index b4c8b8cec..7be85de47 100644 --- a/doc_src/faq.rst +++ b/doc_src/faq.rst @@ -176,7 +176,7 @@ Use the web configuration tool, :ref:`fish_config `, or alter t I accidentally entered a directory path and fish changed directory. What happened? ---------------------------------------------------------------------------------- -If fish is unable to locate a command with a given name, and it starts with '``.``', '``/``' or '``~``', fish will test if a directory of that name exists. If it does, it is implicitly assumed that you want to change working directory. For example, the fastest way to switch to your home directory is to simply press ``~`` and enter. +If fish is unable to locate a command with a given name, and it starts with ``.``, ``/`` or ``~``, fish will test if a directory of that name exists. If it does, it is implicitly assumed that you want to change working directory. For example, the fastest way to switch to your home directory is to simply press ``~`` and enter. The open command doesn't work. ------------------------------ diff --git a/doc_src/index.rst b/doc_src/index.rst index 4a41db5cc..3b48451dd 100644 --- a/doc_src/index.rst +++ b/doc_src/index.rst @@ -100,7 +100,7 @@ This line tells the shell to execute the file with the bash interpreter, that is For a script, written in another language, just replace the interpreter ``/bin/bash`` with the language interpreter of that other language (for example ``/bin/python`` for a ``python`` script) -This line is only needed when scripts are executed without specifying the interpreter. For functions inside fish or when executing a script with ```fish /path/to/script`` they aren't required (but don't hurt either!). +This line is only needed when scripts are executed without specifying the interpreter. For functions inside fish or when executing a script with ``fish /path/to/script`` they aren't required (but don't hurt either!). .. _syntax: @@ -133,11 +133,11 @@ Here is a list of some useful commands: - ``open``, open files with the default application associated with each filetype - ``less``, list the contents of files -Commands and parameters are separated by the space character `' '`. Every command ends with either a newline (i.e. by pressing the return key) or a semicolon '``;``'. More than one command can be written on the same line by separating them with semicolons. +Commands and parameters are separated by the space character ``' '``. Every command ends with either a newline (i.e. by pressing the return key) or a semicolon ``;``. More than one command can be written on the same line by separating them with semicolons. -A switch is a very common special type of argument. Switches almost always start with one or more hyphens '``-``' and alter the way a command operates. For example, the '``ls``' command usually lists all the files and directories in the current working directory, but by using the '``-l``' switch, the behavior of '``ls``' is changed to not only display the filename, but also the size, permissions, owner and modification time of each file. +A switch is a very common special type of argument. Switches almost always start with one or more hyphens ``-`` and alter the way a command operates. For example, the ``ls`` command usually lists all the files and directories in the current working directory, but by using the ``-l`` switch, the behavior of ``ls`` is changed to not only display the filename, but also the size, permissions, owner and modification time of each file. -Switches differ between commands and are documented in the manual page for each command. Some switches are common to most command though, for example '``--help``' will usually display a help text, '``-i``' will often turn on interactive prompting before taking action, while '``-f``' will turn it off. +Switches differ between commands and are documented in the manual page for each command. Some switches are common to most command though, for example ``--help`` will usually display a help text, ``-i`` will often turn on interactive prompting before taking action, while ``-f`` will turn it off. .. _syntax-words: @@ -286,7 +286,7 @@ Example: ``echo Hello 2>output.stderr`` writes the standard error (file descript Piping ------ -The user can string together multiple commands into a *pipeline*. This means that the standard output of one command will be read in as standard input into the next command. This is done by separating the commands by the pipe character '``|``'. For example +The user can string together multiple commands into a *pipeline*. This means that the standard output of one command will be read in as standard input into the next command. This is done by separating the commands by the pipe character ``|``. For example :: @@ -337,20 +337,20 @@ Functions Functions are programs written in the fish syntax. They group together one or more commands and their arguments using a single name. It can also be used to start a specific command with additional arguments. -For example, the following is a function definition that calls the command ``ls`` with the argument '``-l``' to print a detailed listing of the contents of the current directory:: +For example, the following is a function definition that calls the command ``ls`` with the argument ``-l`` to print a detailed listing of the contents of the current directory:: function ll ls -l $argv end -The first line tells fish that a function by the name of ``ll`` is to be defined. To use it, simply write ``ll`` on the commandline. The second line tells fish that the command ``ls -l $argv`` should be called when ``ll`` is invoked. '``$argv``' is a list variable, which always contains all arguments sent to the function. In the example above, these are simply passed on to the ``ls`` command. For more information on functions, see the documentation for the :ref:`function ` builtin. +The first line tells fish that a function by the name of ``ll`` is to be defined. To use it, simply write ``ll`` on the commandline. The second line tells fish that the command ``ls -l $argv`` should be called when ``ll`` is invoked. ``$argv`` is a list variable, which always contains all arguments sent to the function. In the example above, these are simply passed on to the ``ls`` command. For more information on functions, see the documentation for the :ref:`function ` builtin. .. _syntax-function-wrappers: Defining aliases ---------------- -One of the most common uses for functions is to slightly alter the behavior of an already existing command. For example, one might want to redefine the ``ls`` command to display colors. The switch for turning on colors on GNU systems is '``--color=auto``'. An alias, or wrapper, around ``ls`` might look like this:: +One of the most common uses for functions is to slightly alter the behavior of an already existing command. For example, one might want to redefine the ``ls`` command to display colors. The switch for turning on colors on GNU systems is ``--color=auto``. An alias, or wrapper, around ``ls`` might look like this:: function ls command ls --color=auto $argv @@ -377,7 +377,7 @@ Functions can be defined on the commandline or in a configuration file, but they - If the function definition is changed, all running shells will automatically reload the altered version. - Startup time and memory usage is improved, etc. -When fish needs to load a function, it searches through any directories in the list variable ``$fish_function_path`` for a file with a name consisting of the name of the function plus the suffix '``.fish``' and loads the first it finds. +When fish needs to load a function, it searches through any directories in the list variable ``$fish_function_path`` for a file with a name consisting of the name of the function plus the suffix ``.fish`` and loads the first it finds. By default ``$fish_function_path`` contains the following: @@ -437,11 +437,11 @@ Wildcards If a star (``*``) or a question mark (``?``) is present in the parameter, ``fish`` attempts to match the given parameter to any files in such a way that: -- ``*`` can match any string of characters not containing '/'. This includes matching an empty string. +- ``*`` can match any string of characters not containing ``/``. This includes matching an empty string. -- ``**`` matches any string of characters. This includes matching an empty string. The matched string may include the ``/`` character; that is, it recurses into subdirectories. Note that augmenting this wildcard with other strings will not match files in the current working directory (``$PWD``) if you separate the strings with a slash ("/"). This is unlike other shells such as zsh. For example, ``**\/*.fish`` in zsh will match ``.fish`` files in the PWD but in fish will only match such files in a subdirectory. In fish you should type ``**.fish`` to match files in the PWD as well as subdirectories. +- ``**`` matches any string of characters. This includes matching an empty string. The matched string may include the ``/`` character; that is, it recurses into subdirectories. Note that augmenting this wildcard with other strings will not match files in the current working directory (``$PWD``) if you separate the strings with a slash (``/``). This is unlike other shells such as zsh. For example, ``**\/*.fish`` in zsh will match ``.fish`` files in the PWD but in fish will only match such files in a subdirectory. In fish you should type ``**.fish`` to match files in the PWD as well as subdirectories. -- ``?`` can match any single character except '/'. This is deprecated and can be disabled via the `qmark-noglob` :ref:`feature flag`, so `?` will just be an ordinary character. +- ``?`` can match any single character except ``/``. This is deprecated and can be disabled via the `qmark-noglob` :ref:`feature flag`, so `?` will just be an ordinary character. Other shells, such as zsh, provide a rich glob syntax for restricting the files matched by globs. For example, ``**(.)``, to only match regular files. Fish prefers to defer such features to programs, such as ``find``, rather than reinventing the wheel. Thus, if you want to limit the wildcard expansion to just regular files the fish approach is to define and use a function. For example, @@ -671,7 +671,7 @@ Index range expansion Sometimes it's necessary to access only some of the elements of a list, or some of the lines a command substitution outputs. Both allow this by providing a set of indices in square brackets. -Sequences of elements can be written with the range operator '``..``'. A range '``a..b``' ('a' and 'b' being integers) 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. Negative range limits are calculated from the end of the list. If an index is too large or small it's silently clamped to one or the size of the list as appropriate. +Sequences of elements can be written with the range operator ``..``. A range ``a..b`` ('a' and 'b' being integers) 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. Negative range limits are calculated from the end of the list. If an index is too large or small it's silently clamped to one or the size of the list as appropriate. If the end is smaller than the start, or the start is larger than the end, range expansion will go in reverse. This is unless exactly one of the given indices is negative, so the direction doesn't change if the list has fewer elements than expected. @@ -814,11 +814,11 @@ There are three kinds of variables in fish: universal, global and local variable Variables can be explicitly set to be universal with the ``-U`` or ``--universal`` switch, global with the ``-g`` or ``--global`` switch, or local with the ``-l`` or ``--local`` switch. The scoping rules when creating or updating a variable are: -- If a variable is explicitly set to a scope (universal, global or local), that setting will be honored. If a variable of the same name exists in a different scope, that variable will not be changed. +- When a scope is explicitly given, it will be used. If a variable of the same name exists in a different scope, that variable will not be changed. -- If a variable is not explicitly set to a scope, but has been previously defined, the variable scope is not changed. +- When no scope is given, but a variable of that name exists, the variable of the smallest scope will be modified. The scope will not be changed. -- If a variable is not explicitly set to a scope and has not been defined, the variable will be local to the currently executing function. Note that this is different from using the ``-l`` or ``--local`` flag. If one of those flags is used, the variable will be local to the most inner currently executing block, while without these the variable will be local to the function. If no function is executing, the variable will be global. +- As a special case, when no scope is given and no variable has been defined the variable will belong to the scope of the currently executing *function*. Note that this is different from the ``--local`` flag``, which would make the variable local to the current *block*. There may be many variables with the same name, but different scopes. When using a variable, the variable scope will be searched from the inside out, i.e. a local variable will be used rather than a global variable with the same name, a global variable will be used rather than a universal variable with the same name. @@ -829,10 +829,13 @@ The following code will not output anything:: begin # This is a nice local scope where all variables will die set -l pirate 'There be treasure in them thar hills' + set captain Space, the final frontier end echo $pirate # This will not output anything, since the pirate was local + echo $captain + # This will output the good Captain's speech since $captain had function-scope. .. _variables-override: @@ -861,7 +864,7 @@ This syntax is supported since fish 3.1. More on universal variables --------------------------- -Universal variables are variables that are shared between all the users' fish sessions on the computer. Fish stores many of its configuration options as universal variables. This means that in order to change fish settings, all you have to do is change the variable value once, and it will be automatically updated for all sessions, and preserved across computer reboots and login/logout. +Universal variables are variables that are shared between all the user's fish sessions on the computer. Fish stores many of its configuration options as universal variables. This means that in order to change fish settings, all you have to do is change the variable value once, and it will be automatically updated for all sessions, and preserved across computer reboots and login/logout. To see universal variables in action, start two fish sessions side by side, and issue the following command in one of them ``set fish_color_cwd blue``. Since ``fish_color_cwd`` is a universal variable, the color of the current working directory listing in the prompt will instantly change to blue on both terminals. @@ -1059,6 +1062,8 @@ The user can change the settings of ``fish`` by changing the values of certain v - ``pipestatus``, a list of exit statuses of all processes that made up the last executed pipe. +- ``fish_kill_signal``, the signal that terminated the last foreground job, or `0` if the job exited normally. + - ``USER``, the current username. This variable can be changed by the user. - ``CMD_DURATION``, the runtime of the last command in milliseconds. @@ -1232,9 +1237,9 @@ Autosuggestions are a powerful way to quickly summon frequently entered commands Tab Completion -------------- -Tab completion is one of the most time saving features of any modern shell. By tapping the tab key, the user asks ``fish`` to guess the rest of the command or parameter that the user is currently typing. If ``fish`` can only find one possible completion, ``fish`` will write it out. If there is more than one completion, ``fish`` will write out the longest prefix that all completions have in common. If the completions differ on the first character, a list of all possible completions is printed. The list features descriptions of the completions and if the list doesn't fit the screen, it is scrollable by using the arrow keys, the page up/page down keys, the tab key or the space bar. +Tab completion is one of the most time saving features of any modern shell. By tapping :kbd:`Tab`, the user asks ``fish`` to guess the rest of the command or parameter that the user is currently typing. If ``fish`` can only find one possible completion, ``fish`` will write it out. If there is more than one completion, ``fish`` will write out the longest prefix that all completions have in common. If the completions differ on the first character, a list of all possible completions is printed. The list features descriptions of the completions and if the list doesn't fit the screen, it is scrollable by using the arrow keys, :kbd:`Page Up` / :kbd:`Page Down`, :kbd:`Tab` or :kbd:`Space`. -If the list is visible, pressing control-S (or the ``pager-toggle-search`` binding) will allow filtering the list. Shift-tab (or the ``complete-and-search`` binding) will trigger completion with the search field immediately visible. +If the list is visible, pressing :kbd:`Control`\ +\ :kbd:`S` (or the ``pager-toggle-search`` binding) will allow filtering the list. :kbd:`Shift`\ +\ :kbd:`Tab` (or the ``complete-and-search`` binding) will trigger completion with the search field immediately visible. These are the general purpose tab completions that ``fish`` provides: - Completion of commands (builtins, functions and regular programs). @@ -1243,7 +1248,7 @@ These are the general purpose tab completions that ``fish`` provides: - Completion of usernames for tilde expansion. -- Completion of filenames, even on strings with wildcards such as '``*``' and '``**``'. +- Completion of filenames, even on strings with wildcards such as ``*`` and ``**``. ``fish`` provides a large number of program specific completions. Most of these completions are simple options like the ``-l`` option for ``ls``, but some are more advanced. The latter include: @@ -1289,7 +1294,7 @@ Some bindings are shared between emacs- and vi-mode because they aren't text edi - :kbd:`Tab` `completes <#tab-completion>`_ the current token. :kbd:`Shift`\ +\ :kbd:`Tab` completes the current token and starts the pager's search mode. -- :kbd:`←` (Left) and :kbd:`→` (Right) move the cursor left or right by one character. If the cursor is already at the end of the line, and an autosuggestion is available, they accept one word of the autosuggestion. +- :kbd:`←` (Left) and :kbd:`→` (Right) move the cursor left or right by one character. If the cursor is already at the end of the line, and an autosuggestion is available, :kbd:`→` accepts the autosuggestion. - :kbd:`Alt`\ +\ :kbd:`←` and :kbd:`Alt`\ +\ :kbd:`→` move the cursor one word left or right (to the next space or punctuation mark), or moves forward/backward in the directory history if the command line is empty. If the cursor is already at the end of the line, and an autosuggestion is available, :kbd:`Alt`\ +\ :kbd:`→` (or :kbd:`Alt`\ +\ :kbd:`F`) accepts the first word in the suggestion. @@ -1319,7 +1324,9 @@ Some bindings are shared between emacs- and vi-mode because they aren't text edi - :kbd:`Alt`\ +\ :kbd:`L` lists the contents of the current directory, unless the cursor is over a directory argument, in which case the contents of that directory will be listed. -- :kbd:`Alt`\ +\ :kbd:`P` adds the string '``| less;``' to the end of the job under the cursor. The result is that the output of the command will be paged. +- :kbd:`Alt`\ +\ :kbd:`O` opens the file at the cursor in a pager. + +- :kbd:`Alt`\ +\ :kbd:`P` adds the string ``&| less;`` to the end of the job under the cursor. The result is that the output of the command will be paged. - :kbd:`Alt`\ +\ :kbd:`W` prints a short description of the command under the cursor. @@ -1329,7 +1336,7 @@ Some bindings are shared between emacs- and vi-mode because they aren't text edi - :kbd:`Alt`\ +\ :kbd:`S` Prepends `sudo` to the current commandline. -- :kbd:`Control`\ +\ :kbd:`Space` Inserts a space without expanding an :ref:`abbreviation `. (for vi-mode this only applies to insert-mode) +- :kbd:`Control`\ +\ :kbd:`Space` Inserts a space without expanding an :ref:`abbreviation `. For vi-mode this only applies to insert-mode. .. _emacs-mode: @@ -1340,11 +1347,11 @@ Emacs mode commands - :kbd:`End` or :kbd:`Control`\ +\ :kbd:`E` moves to the end of line. If the cursor is already at the end of the line, and an autosuggestion is available, :kbd:`End` or :kbd:`Control`\ +\ :kbd:`E` accepts the autosuggestion. -- :kbd:`Control`\ +\ :kbd:`B`, :kbd:`Control`\ +\ :kbd:`F` move the cursor one character left or right just like the :kbd:`←` (Left) and :kbd:`→` (Right) shared bindings (which are available as well). +- :kbd:`Control`\ +\ :kbd:`B`, :kbd:`Control`\ +\ :kbd:`F` move the cursor one character left or right or accept the autosuggestion just like the :kbd:`←` (Left) and :kbd:`→` (Right) shared bindings (which are available as well). - :kbd:`Control`\ +\ :kbd:`N`, :kbd:`Control`\ +\ :kbd:`P` move the cursor up/down or through history, like the up and down arrow shared bindings. -- :kbd:`Delete` and :kbd:`Backspace` removes one character forwards or backwards respectively. +- :kbd:`Delete` or :kbd:`Backspace` removes one character forwards or backwards respectively. - :kbd:`Control`\ +\ :kbd:`K` moves contents from the cursor to the end of line to the `killring <#killring>`__. @@ -1352,13 +1359,13 @@ Emacs mode commands - :kbd:`Alt`\ +\ :kbd:`U` makes the current word uppercase. -- :kbd:`Control`\ +\ :kbd:`T` transposes the last two characters +- :kbd:`Control`\ +\ :kbd:`T` transposes the last two characters. -- :kbd:`Alt`\ +\ :kbd:`T` transposes the last two words +- :kbd:`Alt`\ +\ :kbd:`T` transposes the last two words. -- :kbd:`Control`\ +\ :kbd:`_` (:kbd:`Control`\ +\ :kbd:`/` on some terminals) undoes the most recent edit of the line +- :kbd:`Control`\ +\ :kbd:`_` (:kbd:`Control`\ +\ :kbd:`/` on some terminals) undoes the most recent edit of the line. -- :kbd:`Alt`\ +\ :kbd:`/` reverts the most recent undo +- :kbd:`Alt`\ +\ :kbd:`/` reverts the most recent undo. You can change these key bindings using the :ref:`bind ` builtin. @@ -1470,10 +1477,25 @@ In addition, when pasting inside single quotes, pasted single quotes and backsla .. [#] These rely on external tools. Currently xsel, xclip, wl-copy/wl-paste and pbcopy/pbpaste are supported. +.. _multiline: + +Multiline editing +----------------- + +The fish commandline editor can be used to work on commands that are several lines long. There are three ways to make a command span more than a single line: + +- Pressing the :kbd:`Enter` key while a block of commands is unclosed, such as when one or more block commands such as ``for``, ``begin`` or ``if`` do not have a corresponding :ref:`end ` command. + +- Pressing :kbd:`Alt`\ +\ :kbd:`Enter` instead of pressing the :kbd:`Enter` key. + +- By inserting a backslash (``\``) character before pressing the :kbd:`Enter` key, escaping the newline. + +The fish commandline editor works exactly the same in single line mode and in multiline mode. To move between lines use the left and right arrow keys and other such keyboard shortcuts. + .. _history-search: -Searchable history ------------------- +Searchable command history +-------------------------- After a command has been entered, it is inserted at the end of a history list. Any duplicate history items are automatically removed. By pressing the up and down keys, the user can search forwards and backwards in the history. If the current command line is not empty when starting a history search, only the commands containing the string entered into the command line are shown. @@ -1496,21 +1518,35 @@ To search for previous entries containing the word 'make', type ``make`` in the If the commandline reads ``cd m``, place the cursor over the ``m`` character and press :kbd:`Alt`\ +\ :kbd:`↑` to search for previously typed words containing 'm'. +Navigating directories +====================== -.. _multiline: +.. _directory-history: -Multiline editing +The current working directory can be displayed with the :ref:`pwd ` command. + +Directory history ----------------- -The fish commandline editor can be used to work on commands that are several lines long. There are three ways to make a command span more than a single line: +Fish automatically keeps a trail of the recent visited directories with :ref:`cd ` by storing this history in the ``dirprev`` and ``dirnext`` variables. -- Pressing the :kbd:`Enter` key while a block of commands is unclosed, such as when one or more block commands such as ``for``, ``begin`` or ``if`` do not have a corresponding :ref:`end ` command. +Several commands are provided to interact with this directory history: -- Pressing :kbd:`Alt`\ +\ :kbd:`Enter` instead of pressing the :kbd:`Enter` key. +- :ref:`dirh ` prints the history +- :ref:`cdh ` displays a prompt to quickly navigate the history +- :ref:`prevd ` moves backward through the history. It is bound to :kbd:`Alt`\ +\ :kbd:`←` +- :ref:`nextd ` moves forward through the history. It is bound to :kbd:`Alt`\ +\ :kbd:`→` -- By inserting a backslash (``\``) character before pressing the :kbd:`Enter` key, escaping the newline. +.. _directory-stack: -The fish commandline editor works exactly the same in single line mode and in multiline mode. To move between lines use the left and right arrow keys and other such keyboard shortcuts. +Directory stack +--------------- + +Another set of commands, usually also available in other shells like bash, deal with the directory stack. Stack handling is not automatic and needs explicit calls of the following commands: + +- :ref:`dirs ` prints the stack +- :ref:`pushd ` adds a directory on top of the stack and makes it the current working directory +- :ref:`popd ` removes the directory on top of the stack and changes the current working directory .. _job-control: diff --git a/doc_src/tutorial.rst b/doc_src/tutorial.rst index 797c1d806..dd71fbeec 100644 --- a/doc_src/tutorial.rst +++ b/doc_src/tutorial.rst @@ -31,7 +31,7 @@ This prompt that you see above is the ``fish`` default prompt: it shows your use - to change this prompt see `how to change your prompt <#prompt>`_ - to switch to fish permanently see `switch your default shell to fish <#switching-to-fish>`_. -From now on, we'll pretend your prompt is just a '``>``' to save space. +From now on, we'll pretend your prompt is just a ``>`` to save space. Learning fish @@ -101,7 +101,7 @@ A command may be invalid because it does not exist, or refers to a file that you > cat :underline:`~/somefi` -This tells you that there exists a file that starts with '``somefi``', which is useful feedback as you type. +This tells you that there exists a file that starts with ``somefi``, which is useful feedback as you type. These colors, and many more, can be changed by running ``fish_config``, or by modifying variables directly. diff --git a/fish.pc.in b/fish.pc.in index 7a96e72c6..dcdb748de 100644 --- a/fish.pc.in +++ b/fish.pc.in @@ -1,8 +1,8 @@ prefix=@prefix@ -datadir=@datadir@ -completionsdir=@extra_completionsdir@ -functionsdir=@extra_functionsdir@ -confdir=@extra_confdir@ +datadir=${prefix}/@rel_datadir@ +completionsdir=${datadir}/@rel_completionsdir@ +functionsdir=${datadir}/@rel_functionsdir@ +confdir=${datadir}/@rel_confdir@ Name: fish Description: fish, the friendly interactive shell diff --git a/fish.spec.in b/fish.spec.in index cba6400fd..1a0e3e33b 100644 --- a/fish.spec.in +++ b/fish.spec.in @@ -22,7 +22,7 @@ BuildRequires: update-desktop-files %endif %if 0%{?opensuse_bs} && 0%{?rhel} && 0%{?rhel} < 7 -BuildRequires: gcc48 gcc48-c++ python-argparse +BuildRequires: gcc48 gcc48-c++ %endif # for tests @@ -95,7 +95,12 @@ cp -a CONTRIBUTING.md %{buildroot}%{_pkgdocdir} %if 0%{?__builddir:1} cd %__builddir %endif +# Turn off tests on RHEL 6, they are hard to keep running +%if 0%{?opensuse_bs} && 0%{?rhel} && 0%{?rhel} < 7 +./fish -c 'exit 0' +%else make test SHOW_INTERACTIVE_LOG=1 +%endif %clean rm -rf $RPM_BUILD_ROOT diff --git a/share/completions/amixer.fish b/share/completions/amixer.fish index 724e3e87c..4ce38fa18 100644 --- a/share/completions/amixer.fish +++ b/share/completions/amixer.fish @@ -1,6 +1,6 @@ set -l cmds 'scontrols scontents controls contents sget sset cset cget set get' complete -c amixer -xa "$cmds" -n "not __fish_seen_subcommand_from $cmds" -complete -c amixer -n '__fish_seen_subcommand_from sset sget get set' -xa "(amixer scontrols | cut --delimiter \' --fields 2)" +complete -c amixer -n '__fish_seen_subcommand_from sset sget get set' -xa "(amixer scontrols | string split -f 2 \')" complete -c amixer -s h -l help -d 'this help' complete -c amixer -s c -l card -r -d 'select the card' diff --git a/share/completions/command.fish b/share/completions/command.fish index 85d4eca3a..ea35aaa06 100644 --- a/share/completions/command.fish +++ b/share/completions/command.fish @@ -2,5 +2,4 @@ complete -c command -n 'test (count (commandline -opc)) -eq 1' -s h -l help -d ' complete -c command -n 'test (count (commandline -opc)) -eq 1' -s a -l all -d 'Print all external commands by the given name' complete -c command -n 'test (count (commandline -opc)) -eq 1' -s q -l quiet -d 'Do not print anything, only set exit status' complete -c command -n 'test (count (commandline -opc)) -eq 1' -s s -l search -d 'Print the file that would be executed' -complete -c command -n 'test (count (commandline -opc)) -eq 1' -s s -l search -d 'Print the file that would be executed' complete -c command -xa "(__fish_complete_subcommand)" diff --git a/share/completions/duply.fish b/share/completions/duply.fish index d1df2a5ac..12259be1b 100644 --- a/share/completions/duply.fish +++ b/share/completions/duply.fish @@ -1,6 +1,6 @@ # First parameter is the profile name, or 'usage' -complete --command duply --no-files --condition __fish_is_first_token --arguments '(/bin/ls /etc/duply 2>/dev/null) (/bin/ls ~/.duply 2>/dev/null)' -d Profile +complete -c duply -f -n __fish_is_first_token - '(set -l files /etc/duply/* ~/.duply/*; string replace -r ".*/" "" -- $files)' -d Profile complete --command duply --no-files --arguments usage -d 'Get usage help text' # Second parameter is a duply command diff --git a/share/completions/fish_key_reader.fish b/share/completions/fish_key_reader.fish new file mode 100644 index 000000000..bab66cac0 --- /dev/null +++ b/share/completions/fish_key_reader.fish @@ -0,0 +1,3 @@ +complete -c fish_key_reader -s h -l help -d 'Display help and exit' +complete -c fish_key_reader -s v -l version -d 'Display version and exit' +complete -c fish_key_reader -s c -l continuous -d 'Start a continuous session' diff --git a/share/completions/fuser.fish b/share/completions/fuser.fish index 83463834a..9d1a17023 100644 --- a/share/completions/fuser.fish +++ b/share/completions/fuser.fish @@ -1,7 +1,6 @@ __fish_make_completion_signals for i in $__kill_signals - set number (echo $i | cut -d " " -f 1) - set name (echo $i | cut -d " " -f 2) + string split -f 1,2 " " -- $i | read --line number name complete -c fuser -o $number -d $name complete -c fuser -o $name -d $name end diff --git a/share/completions/git.fish b/share/completions/git.fish index b99e1948b..f9b4a8a41 100644 --- a/share/completions/git.fish +++ b/share/completions/git.fish @@ -16,7 +16,8 @@ function __fish_git end end # Using 'command git' to avoid interactions for aliases from git to (e.g.) hub - command git $global_args $saved_args + # Using eval to expand ~ and variables specified on the commandline. + eval command git $global_args \$saved_args end # Print an optspec for argparse to handle git's options that are independent of any subcommand. diff --git a/share/completions/groups.fish b/share/completions/groups.fish new file mode 100644 index 000000000..ad9b0ceb9 --- /dev/null +++ b/share/completions/groups.fish @@ -0,0 +1,3 @@ +complete -c groups -x -a "(__fish_complete_users)" +complete -c groups -l help -d 'Display help message' +complete -c groups -l version -d 'Display version information' diff --git a/share/completions/journalctl.fish b/share/completions/journalctl.fish index 33e31f20a..eee6e5596 100644 --- a/share/completions/journalctl.fish +++ b/share/completions/journalctl.fish @@ -16,7 +16,7 @@ function __fish_journalctl_is_field end function __fish_journalctl_field_values - set -l token (commandline -t | cut -d"=" -f 1) + set -l token (commandline -t | string split -f1 =) command journalctl -F $token 2>/dev/null | while read value echo $token=$value end diff --git a/share/completions/make.fish b/share/completions/make.fish index 4f425cd99..52fa2b1f3 100644 --- a/share/completions/make.fish +++ b/share/completions/make.fish @@ -12,7 +12,7 @@ function __fish_print_make_targets --argument-names directory file if make --version 2>/dev/null | string match -q 'GNU*' # https://stackoverflow.com/a/26339924 make $makeflags -pRrq : 2>/dev/null | - awk -F: '/^# Files/,/^# Finished Make data base/ { + awk -F: '/^# Files/,/^# Finished Make data base/ { if ($1 == "# Not a target") skip = 1; if ($1 !~ "^[#.\t]") { if (!skip) print $1; skip=0 } }' 2>/dev/null diff --git a/share/completions/nc.fish b/share/completions/nc.fish index 1a85bbe18..4dc5f18d7 100644 --- a/share/completions/nc.fish +++ b/share/completions/nc.fish @@ -1,51 +1,12 @@ -complete -c nc -d "Remote hostname" -x -a "(__fish_print_hostnames)" +# There a several different implementations of netcat. +# Try to figure out which is the current used one +# and load the right set of completions. -complete -c nc -s 4 -d "Use IPv4 only" -complete -c nc -s 6 -d "Use IPv6 only" -complete -c nc -s U -l unixsock -d "Use Unix domain sockets only" -complete -c nc -l vsock -d "Use vsock sockets only" -complete -c nc -s C -l crlf -d "Use CRLF for EOL sequence" -complete -c nc -s c -l sh-exec -x -d "Executes the given command via /bin/sh" -complete -c nc -s e -l exec -F -d "Executes the given command" -complete -c nc -l lua-exec -F -d "Executes the given Lua script" -complete -c nc -s g -x -d "Loose source routing hop points" -complete -c nc -s G -x -d "Loose source routing hop pointer" -complete -c nc -s m -l max-conns -x -d "Maximum simultaneous connections" -complete -c nc -s h -d "Show help" -complete -c nc -s d -l delay -x -d "Wait between read/writes" -complete -c nc -s o -l output -F -d "Dump session data to a file" -complete -c nc -s x -l hex-dump -F -d "Dump session data as hex to a file" -complete -c nc -s i -l idle-timeout -x -d "Idle read/write timeout" -complete -c nc -s p -l source-port -x -d "Specify source port to use" -complete -c nc -s s -l source -x -d "Specify source address" -complete -c nc -s l -l listen -d "Bind and listen for incoming connections" -complete -c nc -s k -l keep-open -d "Accept multiple connections in listen mode" -complete -c nc -s n -l nodns -d "Do not resolve hostnames via DNS" -complete -c nc -s t -l telnet -d "Answer Telnet negotiation" -complete -c nc -s u -l udp -d "Use UDP instead of default TCP" -complete -c nc -l sctp -d "Use SCTP instead of default TCP" -complete -c nc -s v -l verbose -d "Set verbosity level" -complete -c nc -s w -l wait -x -d "Connect timeout" -complete -c nc -s z -d "Zero-I/O mode, report connection status only" -complete -c nc -l append-output -d "Append rather than clobber specified output files" -complete -c nc -l send-only -d "Only send data, ignoring received" -complete -c nc -l recv-only -d "Only receive data, never send anything" -complete -c nc -l no-shutdown -d "Continue half-duplex when receiving EOF" -complete -c nc -l allow -x -d "Allow only given hosts to connect" -complete -c nc -l allowfile -F -d "A file of hosts allowed to connect" -complete -c nc -l deny -x -d "Deny given hosts from connecting" -complete -c nc -l denyfile -F -d "A file of hosts denied from connecting" -complete -c nc -l broker -d "Enable connection brokering mode" -complete -c nc -l chat -d "Start a simple chat server" -complete -c nc -l proxy -x -d "Specify address of host to proxy through" -complete -c nc -l proxy-type -x -a "http socks4 socks5" -d "Specify proxy type" -complete -c nc -l proxy-auth -x -d "Authenticate with HTTP or SOCKS proxy" -complete -c nc -l proxy-dns -x -a "local remote both none" -d "Specify where to resolve proxy destination" -complete -c nc -l ssl -d "Connect or listen with SSL" -complete -c nc -l ssl-cert -F -d "Specify SSL certificate file" -complete -c nc -l ssl-key -F -d "Specify SSL private key" -complete -c nc -l ssl-verify -d "Verify trust and domain name of certificates" -complete -c nc -l ssl-trustfile -F -d "PEM file containing trusted SSL certificates" -complete -c nc -l ssl-ciphers -x -d "Cipherlist containing SSL ciphers to use" -complete -c nc -l ssl-alpn -x -d "ALPN protocol list to use" -complete -c nc -l version -d "Display version information" +set -l flavor +if string match -rq -- '^OpenBSD netcat' (nc -h 2>&1)[1] + set flavor nc.openbsd +else + set flavor (basename (realpath (command -v nc))) +end + +__fish_complete_netcat nc $flavor diff --git a/share/completions/nc.openbsd.fish b/share/completions/nc.openbsd.fish new file mode 100644 index 000000000..f93163c9b --- /dev/null +++ b/share/completions/nc.openbsd.fish @@ -0,0 +1 @@ +__fish_complete_netcat nc.openbsd diff --git a/share/completions/nc.traditional.fish b/share/completions/nc.traditional.fish new file mode 100644 index 000000000..8d0c10f39 --- /dev/null +++ b/share/completions/nc.traditional.fish @@ -0,0 +1 @@ +__fish_complete_netcat nc.traditional diff --git a/share/completions/ncat.fish b/share/completions/ncat.fish new file mode 100644 index 000000000..186b6bf1c --- /dev/null +++ b/share/completions/ncat.fish @@ -0,0 +1,74 @@ +# Completions for ncat (https://www.nmap.org) + +complete -c ncat -f -a "(__fish_print_hostnames)" + +# PROTOCOL OPTIONS +complete -c ncat -s 4 -d "IPv4 only" +complete -c ncat -s 6 -d "IPv6 only" +complete -c ncat -s U -l unixsock -d "Use Unix domain sockets" +complete -c ncat -s u -l udp -d "Use UDP" +complete -c ncat -l sctp -d "Use SCTP" + +# CONNECT MODE OPTIONS +complete -c ncat -s g -x -d "Loose source routing" +complete -c ncat -s G -x -d "Set source routing pointer" +complete -c ncat -s p -l source-port -x -d "Specify source port" +complete -c ncat -s s -l source -x -d "Specify source address" + +# LISTEN MODE OPTIONS +complete -c ncat -s l -l listen -d "Listen for connections" +complete -c ncat -s m -l max-conns -x -d "Specify maximum number of connections" +complete -c ncat -s k -l keep-open -d "Accept multiple connections" +complete -c ncat -l broker -d "Connection brokering" +complete -c ncat -l chat -d "Ad-hoc \"chat server\"" + +# SSL OPTIONS +complete -c ncat -l ssl -d "Use SSL" +complete -c ncat -l ssl-verify -d "Verify server certificates" +complete -c ncat -l ssl-cert -r -d "Specify SSL certificate" +complete -c ncat -l ssl-key -r -d "Specify SSL private key" +complete -c ncat -l ssl-trustfile -r -d "List trusted certificates" +function __fish_complete_openssl_ciphers + openssl ciphers -s -stdname | string replace -r '^([^ ]*) - ([^ ]*).*$' '$2\t$1' + for cs in COMPLEMENTOFDEFAULT ALL COMPLEMENTOFALL HIGH MEDIUM LOW eNULL NULL aNULL kRSA aRSA RSA kDHr kDHd kDH kDHE kEDH DH DHE EDH ADH kEECDH kECDHE ECDH ECDHE EECDH AECDH aDSS DSS aDH aECDSA ECDSA TLSv1.2 TLSv1.0 SSLv3 AES128 AES256 AES AESGCM AESCCM AESCCM8 ARIA128 ARIA256 ARIA CAMELLIA128 CAMELLIA256 CAMELLIA CHACHA20 3DES DES RC4 RC2 IDEA SEED MD5 SHA1 SHA SHA256 SHA384 aGOST aGOST01 kGOST GOST94 GOST89MAC PSK kPSK kECDHEPSK kDHEPSK kRSAPSK aPSK SUITEB128 SUITEB128ONLY SUITEB192 + printf "%s\tCipher String\n" $cs + end +end +complete -c ncat -l ssl-ciphers -x -a "(__fish_complete_list : __fish_complete_openssl_ciphers)" -d "Specify SSL ciphersuites" +complete -c ncat -l ssl-alpn -x -d "Specify ALPN protocol list" + +# PROXY OPTIONS +complete -c ncat -l proxy -x -d "Specify proxy address" +complete -c ncat -l proxy-type -x -d "Specify proxy protocol" +complete -c ncat -l proxy-auth -x -d "Specify proxy credentials" + +# COMMAND EXECUTION OPTIONS +complete -c ncat -s e -l exec -r -d "Execute command" +complete -c ncat -s c -l sh-exec -r -d "Execute command via sh" +complete -c ncat -l lua-exec -r -d "Execute a .lua script" + +# ACCESS CONTROL OPTIONS +complete -c ncat -l allow -x -a "(__fish_print_hostnames)" -d "Allow connections" +complete -c ncat -l allowfile -r -d "Allow connections from file" +complete -c ncat -l deny -x -a "(__fish_print_hostnames)" -d "Deny connections" +complete -c ncat -l denyfile -r -d "Deny connections from file" + +# TIMING OPTIONS +complete -c ncat -s d -l delay -x -d "Specify line delay" +complete -c ncat -s i -l idle-timeout -x -d "Specify idle timeout" +complete -c ncat -s w -l wait -x -d "Specify connect timeout" + +# OUTPUT OPTIONS +complete -c ncat -s o -l output -r -d "Save session data" +complete -c ncat -s x -l hex-dump -r -d "Save session data in hex" +complete -c ncat -l append-output -d "Append output" +complete -c ncat -s v -l verbose -d "Be verbose" + +# MISC OPTIONS +complete -c ncat -s C -l crlf -d "Use CRLF as EOL" +complete -c ncat -s h -l help -d "Help screen" +complete -c ncat -l -recv-only -d "Only receive data" +complete -c ncat -l send-only -d "Only send data" +complete -c ncat -l no-shutdown -d "Do not shutdown into half-duplex mode" +complete -c ncat -s t -l telnet -d "Answer Telnet negotiations" +complete -c ncat -l version -d "Display version" diff --git a/share/completions/netcat.fish b/share/completions/netcat.fish new file mode 100644 index 000000000..a33e8e21e --- /dev/null +++ b/share/completions/netcat.fish @@ -0,0 +1,12 @@ +# There a several different implementations of netcat. +# Try to figure out which is the current used one +# and load the right set of completions. + +set -l flavor +if string match -rq -- '^OpenBSD netcat' (netcat -h 2>&1)[1] + set flavor nc.openbsd +else + set flavor (basename (realpath (command -v netcat))) +end + +__fish_complete_netcat netcat $flavor diff --git a/share/completions/nextd.fish b/share/completions/nextd.fish index be515cc51..dff14470d 100644 --- a/share/completions/nextd.fish +++ b/share/completions/nextd.fish @@ -1 +1 @@ -complete -c nextd -s l -d "Also print directory history" +complete -c nextd -s l -l list -d "Also print directory history" diff --git a/share/completions/ninja.fish b/share/completions/ninja.fish index f1b739589..706627deb 100644 --- a/share/completions/ninja.fish +++ b/share/completions/ninja.fish @@ -1,11 +1,10 @@ function __fish_ninja set -l saved_args $argv set -l dir . - argparse -i C/dir= -- (commandline -opc) - and set -ql _flag_C - and set -l dir $_flag_C - test -f $dir/build.ninja - and command ninja -C$dir $saved_args + if argparse -i C/dir= -- (commandline -opc) + # Using eval to expand ~ and variables specified on the commandline. + eval command ninja -C$_flag_C \$saved_args + end end function __fish_print_ninja_tools diff --git a/share/completions/nmap.fish b/share/completions/nmap.fish new file mode 100644 index 000000000..2c63fd93b --- /dev/null +++ b/share/completions/nmap.fish @@ -0,0 +1,180 @@ +# Completions for nmap (https://www.nmap.org) + +complete -c nmap -f -a "(__fish_print_hostnames)" + +# TARGET SPECIFICATION +complete -c nmap -o iL -F -d 'Input target from file' +complete -c nmap -o iR -x -d 'Choose random targets' +complete -c nmap -l exclude -x -a "(__fish_print_hostnames)" -d 'Exclude hosts/networks' +complete -c nmap -l excludefile -r -d 'Exclude list from file' + +# HOST DISCOVERY +complete -c nmap -o sL -d 'Scan: List Scan' +complete -c nmap -o sn -d 'Scan: No port scan' +complete -c nmap -o Pn -d 'Probe: No Ping' +complete -c nmap -o PS -x -d 'Probe: TCP Syn Ping' +complete -c nmap -o PA -x -d 'Probe: TCP ACK Ping' +complete -c nmap -o PU -x -d 'Probe: UDP Ping' +complete -c nmap -o PY -x -d 'Probe: SCTP INIT Ping' +complete -c nmap -o PE -d 'Probe: ICMP Echo Ping' +complete -c nmap -o PP -d 'Probe: ICMP timestamp request' +complete -c nmap -o PM -d 'Probe: ICMP netmask Ping' +complete -c nmap -o PO -x -d 'Probe: IP Protocol Ping' +complete -c nmap -o PR -d 'Probe: ARP Ping' +complete -c nmap -l disable-arp-ping -d 'No ARP or ND Ping' +complete -c nmap -l traceroute -d 'Trace path to host' +complete -c nmap -s n -d 'No DNS resolution' +complete -c nmap -s R -d 'DNS resolution for all targets' +complete -c nmap -l system-dns -d 'Use system DNS resolver' +complete -c nmap -l dns-servers -x -a "(__fish_print_hostnames)" -d 'Servers to use for reverse DNS queries' + +# PORT SCANNING TECHNIQUES +complete -c nmap -o sS -d 'Scan: TCP SYN' +complete -c nmap -o sT -d 'Scan: TCP connect' +complete -c nmap -o sU -d 'Scan: UDP' +complete -c nmap -o sY -d 'Scan: SCTP INIT' +complete -c nmap -o sN -d 'Scan: TCP NULL' +complete -c nmap -o sF -d 'Scan: FIN' +complete -c nmap -o sX -d 'Scan: Xmas' +complete -c nmap -o sA -d 'Scan: ACK' +complete -c nmap -o sW -d 'Scan: Window' +complete -c nmap -o sM -d 'Scan: Mainmon' +complete -c nmap -l scanflags -d 'Custom TCP scan flags' +complete -c nmap -o sZ -d 'Scan: SCTP COOKIE ECHO' +complete -c nmap -o sI -x -a"(__fish_print_hostnames)" -d 'Scan: Idle Scan' +complete -c nmap -o sO -d 'Scan: IP protocol' +complete -c nmap -s b -x -a"(__fish_print_hostnames)" -d 'FTP bounce scan' + +# PORT SPECIFICATION AND SCAN ORDER +complete -c nmap -s p -d 'Only scan specified ports' +complete -c nmap -l exclude-ports -d 'Exclude the specified ports from scanning' +complete -c nmap -s F -d 'Fast (limited port) scan' +complete -c nmap -s r -d "Don't randomize ports" +complete -c nmap -l port-ratio -x -d 'Scan ports with ratio greater then' +complete -c nmap -l top-ports -x -d 'Scan the n highest-ratio ports' + +# SERVICE AND VERSION DETECTION +complete -c nmap -o sV -d 'Scan: Version' +complete -c nmap -l allports -d "Don't exclude any ports from version detection" +complete -c nmap -l version-intensity -x -d 'Set version scan intensity' +complete -c nmap -l version-light -d 'Enable light mode' +complete -c nmap -l version-all -d 'Try every single probe' +complete -c nmap -l version-trace -d 'Trace version scan activity' + +# OS DETECTION +complete -c nmap -s O -d 'Enable OS detection' +complete -c nmap -l osscan-limit -d 'Limit OS detection to promising targets' +complete -c nmap -l osscan-guess -d 'Guess OS detection results' +complete -c nmap -l fuzzy -d 'Guess OS detection results' +complete -c nmap -l max-os-tries -d 'Set the maximum number of OS detection tries against a target' + +# NMAP SCRIPTING ENGINE (NSE) +complete -c nmap -o sC -d 'Scan: Scripts (default)' +function __fish_complete_nmap_script + # cache completion for 5 minutes (`nmap --script-help all` is slow) + if test -z "$__fish_nmap_script_completion_cache" -o (date -d "now - 5min" +"%s") -gt "$__fish_nmap_script_completion_cache_time" + set -g __fish_nmap_script_completion_cache_time (date +"%s") + set -g __fish_nmap_script_completion_cache "" + set -l cmd + for l in (nmap --script-help all|grep -A2 -B1 Categories:|grep -v '^\\(--\\|Categories:\\|https:\\)') + if string match -q -v --regex "^ " $l + set cmd $l + else + set __fish_nmap_script_completion_cache $__fish_nmap_script_completion_cache\n$cmd\t(string trim -l $l) + end + end + for cat in all auth broadcast brute default discovery dos exploit external fuzzer intrusive malware safe version vuln + set __fish_nmap_script_completion_cache $__fish_nmap_script_completion_cache\n$cat\tCategory\n + end + end + echo -e $__fish_nmap_script_completion_cache +end +complete -c nmap -l script -r -a "(__fish_complete_list , __fish_complete_nmap_script)" +complete -c nmap -l script -r -d 'Runs a script scan' +complete -c nmap -l script-args -d 'provide arguments to NSE scripts' +complete -c nmap -l script-args-file -r -d 'load arguments to NSE scripts from a file' +complete -c nmap -l script-help -r -a "(__fish_complete_list , __fish_complete_nmap_script)" +complete -c nmap -l script-help -r -d "Shows help about scripts" +complete -c nmap -l script-trace +complete -c nmap -l script-updatedb + +# TIMING AND PERFORMANCE +complete -c nmap -l min-hostgroup -l max-hostgroup -x -d 'Adjust paralel scan group size' +complete -c nmap -l min-parallelism -l max-parallelism -x -d 'Adjust probe parallelization' +complete -c nmap -l min-rtt-timeout -l max-rtt-timeout -l initial-rtt-timeout -x -d 'Adjust probe timeouts' +complete -c nmap -l max-retries -x -d 'Specify the maximum number of port scan probe retransmissions' +complete -c nmap -l host-timeout -d 'to skip slow hosts' +complete -c nmap -l script-timeout -x +complete -c nmap -l scan-delay -l max-scan-delay -x -d 'Adjust delay between probes' +complete -c nmap -l min-rate -l max-rate -x -d 'Directly control the scanning rate' +complete -c nmap -l defeat-rst-ratelimit -d 'ignore ICMP-RST rate limits' +complete -c nmap -l defeat-icmp-ratelimit -d 'ignore ICMP unreachable in UDP' +complete -c nmap -l nsock-engine -x -d 'Enforce use of a given nsock IO multiplexing engine' -a "epoll kqueue poll select" +function __fish_complete_nmap_timing-template + set -l i 0 + for t in paranoid sneaky polite normal aggressive insane + printf "%i\t%s timing\n" $i $t + printf "%s\tTemplate %i\n" $t $i + set i (math $i + 1) + end +end +complete -c nmap -s T -x -a "(__fish_complete_nmap_timing-template)" -d 'Set a timing template' + +# FIREWALL/IDS EVASION AND SPOOFING +complete -c nmap -s f -d 'fragment packets' -n "not __fish_contains_opt -s f" +complete -c nmap -s f -d 'use 16 bytes per fragment' -n "__fish_contains_opt -s f" +complete -c nmap -l mtu -d 'use specified mtu' -n "__fish_contains_opt -s f" +complete -c nmap -s D -x -d 'Cloak a scan with decoys' +complete -c nmap -s S -x -d 'Spoof source address' +complete -c nmap -s e -x -d 'Use specified interface' -a "(__fish_print_interfaces)" +complete -c nmap -l source-port -s g -x -d 'Spoof source port number' +complete -c nmap -l data -x -d 'Append custom binary data to sent packets' +complete -c nmap -l data-string -x -d 'Append custom string to sent packets' +complete -c nmap -l data-length -x -d 'Append random data to sent packets' +function __fish_complete_nmap_ip-options + printf "S\tstrict source routing\n" # may be followed by ip addresses + printf "R\trecord route\n" # may be followed by ip addresses + printf "L\tloose source routing\n" # may be followed by ip addresses + printf "T\trecord internet timestamps\n" + printf "U\trecord timestamps and ip addresses\n" +end +complete -c nmap -l ip-options -x -a "(__fish_complete_nmap_ip-options)" -d 'Send packets with specified ip options' +complete -c nmap -l ttl -x -d 'Set IP time-to-live field' +complete -c nmap -l randomize-hosts -d 'Randomize target host order' +complete -c nmap -l spoof-mac -x -d 'Spoof MAC address' +complete -c nmap -l proxies -x -d 'Relay TCP connections through a chain of proxies' +complete -c nmap -l badsum -d 'Send packets with bogus TCP/UDP checksums' +complete -c nmap -l adler32 -d 'Use deprecated Adler32 instead of CRC32C for SCTP checksums' + +# OUTPUT +complete -c nmap -o oN -r -d 'normal output' +complete -c nmap -o oX -r -d 'XML output' +complete -c nmap -o oS -r -d 'ScRipT KIdd|3 oUTpuT' +complete -c nmap -o oG -r -d 'grepable output' +complete -c nmap -o oA -r -d 'Output in the three major formats' +complete -c nmap -s v -d 'Increase/Set verbosity level' +complete -c nmap -s d -d 'Increase/Set debugging level' -a '0 1 2 3 4 5 6 7 8 9' +complete -c nmap -l reason -d 'Host and port state reasons' +complete -c nmap -l stats-every -x -d 'Print periodic timing stats' +complete -c nmap -l packet-trace -d 'Trace packets and data sent and received' +complete -c nmap -l open -d 'Show only open (or possibly open) ports' +complete -c nmap -l iflist -d 'List interfaces and routes' +complete -c nmap -l append-output -d 'Append to rather than clobber output files' +complete -c nmap -l resume -r -d 'Resume aborted scan' +complete -c nmap -l stylesheet -r -d 'Set XSL stylesheet to transform XML output' +complete -c nmap -l webxml -d 'Load stylesheet from Nmap.Org' +complete -c nmap -l no-stylesheet -d 'Omit XSL stylesheet declaration from XML' + +# MISCELLANEOUS OPTIONS +complete -c nmap -s 6 -d 'Enable IPv6 scanning' +complete -c nmap -s A -d 'Aggressive scan options' +complete -c nmap -l datadir -x -a "(__fish_complete_directories)" -d 'Specify custom Nmap data file location' +complete -c nmap -l servicedb -r -d 'Specify custom services file' +complete -c nmap -l versiondb -r -d 'Specify custom service probes file' +complete -c nmap -l send-eth -d 'Use raw ethernet sending' +complete -c nmap -l send-ip -d 'Send at raw IP level' +complete -c nmap -l privileged -d 'Assume that the user is fully privileged' +complete -c nmap -l unprivileged -d 'Assume that the user lacks raw socket privileges' +complete -c nmap -l release-memory -d 'Release memory before quitting' +complete -c nmap -s V -l version -d 'Print version number' +complete -c nmap -s h -l help -d 'Print help summary page' diff --git a/share/completions/npm.fish b/share/completions/npm.fish index 601f9c5b2..e9ec20990 100644 --- a/share/completions/npm.fish +++ b/share/completions/npm.fish @@ -50,7 +50,7 @@ function __fish_complete_npm -d "Complete the commandline using npm's 'completio if command -sq npm # npm completion is bash-centric, so we need to translate fish's "commandline" stuff to bash's $COMP_* stuff # COMP_LINE is an array with the words in the commandline - set -lx COMP_LINE (commandline -o) + set -lx COMP_LINE (commandline -opc) # COMP_CWORD is the index of the current word in COMP_LINE # bash starts arrays with 0, so subtract 1 set -lx COMP_CWORD (math (count $COMP_LINE) - 1) diff --git a/share/completions/openssl.fish b/share/completions/openssl.fish new file mode 100644 index 000000000..676daf853 --- /dev/null +++ b/share/completions/openssl.fish @@ -0,0 +1,7 @@ +function __fish_openssl_subcommand_options --description "Print options for openssl subcommand" + set -l cmd (commandline -poc) + openssl list -options $cmd[2] | string replace -r -- '^(\S*)\s*.*' '-$1' +end + +complete -c openssl -n __fish_use_subcommand -x -a "(openssl list -1 -commands -cipher-commands -digest-commands)" +complete -c openssl -n 'not __fish_use_subcommand && string match -qr -- "^-" (commandline -ct)' -a "(__fish_openssl_subcommand_options)" diff --git a/share/completions/phpunit.fish b/share/completions/phpunit.fish index 8cb18caaf..56c51c86d 100644 --- a/share/completions/phpunit.fish +++ b/share/completions/phpunit.fish @@ -11,7 +11,7 @@ end # Lists PHPUnit objects corresponding to the given option function __fish_phpunit_list --argument option # Use the same PHPUnit binary as in the command being completed - set -l phpunit (commandline -o)[1] + set -l phpunit (commandline -opc)[1] test -x $phpunit or return diff --git a/share/completions/prevd.fish b/share/completions/prevd.fish index 96cad2949..18affa669 100644 --- a/share/completions/prevd.fish +++ b/share/completions/prevd.fish @@ -1 +1 @@ -complete -c prevd -s l -d "Also print directory history" +complete -c prevd -s l -l list -d "Also print directory history" diff --git a/share/completions/rmmod.fish b/share/completions/rmmod.fish index 4f03e9890..bd6e2fdaa 100644 --- a/share/completions/rmmod.fish +++ b/share/completions/rmmod.fish @@ -1,8 +1,8 @@ # rmmod completion -complete -c rmmod -x -a "(/sbin/lsmod | awk 'NR > 1 {print \$1}')" +complete -c rmmod -x -a "(lsmod | string replace -r '\s.*' '')" -complete -c rmmod -s h -l help -d "Prints the help text." -complete -c rmmod -s s -l syslog -d "Send errors to syslog instead of standard error." -complete -c rmmod -s v -l verbose -d "Print messages about what the program is doing." +complete -c rmmod -s h -l help -d "Prints the help text" +complete -c rmmod -s s -l syslog -d "Send errors to syslog instead of standard error" +complete -c rmmod -s v -l verbose -d "Print messages about what the program is doing" complete -c rmmod -s V -l version -d "Show version of program and exit" complete -c rmmod -s f -l force -d "With this option, you can remove modules which are being used, or which are not designed to be removed, or have been marked as unsafe" diff --git a/share/completions/sftp.fish b/share/completions/sftp.fish new file mode 100644 index 000000000..065500534 --- /dev/null +++ b/share/completions/sftp.fish @@ -0,0 +1,24 @@ +complete -c sftp -x -a "(__fish_complete_user_at_hosts)" + +complete -c sftp -s 4 -d 'Use IPv4 addresses only' +complete -c sftp -s 6 -d 'Use IPv6 addresses only' +complete -c sftp -s a -d 'Attempt to continue interrupted transfers' +complete -c sftp -s B -x -d 'Size of the buffer when transferring files' +complete -c sftp -s b -F -d 'Reads a series of commands from an input batchfile' +complete -c sftp -s C -d 'Enables compression' +complete -c sftp -s c -x -d 'The cipher to use for encrypting data' +complete -c sftp -s D -x -d 'Connect directly to a local sftp server' +complete -c sftp -s F -F -d 'Alternative per-user configuration file' +complete -c sftp -s f -d 'Flush files to disk after transfer' +complete -c sftp -s i -F -d 'Identity (private key) file' +complete -c sftp -s J -x -a "(__fish_complete_user_at_hosts)" -d 'ProxyJump host' +complete -c sftp -s l -x -d 'Limits the used bandwidth (Kbit/s)' +complete -c sftp -s o -x -d 'Set additional ssh_config options' +complete -c sftp -s P -x -d 'Port to connect to on the remote host' +complete -c sftp -s p -d 'Preserve timestamps from the original files transferred' +complete -c sftp -s q -d 'Quiet mode' +complete -c sftp -s R -x -d 'How many requests may be outstanding' +complete -c sftp -s r -d 'Recursively copy entire directories' +complete -c sftp -s S -r -d 'Program to use for the encrypted connection' +complete -c sftp -s s -x -d 'The SSH2 subsystem or the path for an sftp server' +complete -c sftp -s v -d 'Raise logging level' diff --git a/share/completions/string.fish b/share/completions/string.fish index 6c4de96f1..c60ccc1c5 100644 --- a/share/completions/string.fish +++ b/share/completions/string.fish @@ -13,6 +13,7 @@ complete -x -c string -n "test (count (commandline -opc)) -ge 2; and contains -- complete -f -c string -n "test (count (commandline -opc)) -lt 2" -a split complete -f -c string -n "test (count (commandline -opc)) -lt 2" -a split0 complete -x -c string -n 'test (count (commandline -opc)) -ge 2; and string match -qr split0\?\$ -- (commandline -opc)[2]' -s m -l max -a "(seq 1 10)" -d "Specify maximum number of splits" +complete -x -c string -n 'test (count (commandline -opc)) -ge 2; and string match -qr split0\?\$ -- (commandline -opc)[2]' -s f -l fields -a "(seq 1 10)" -d "Specify fields" complete -f -c string -n 'test (count (commandline -opc)) -ge 2; and string match -qr split0\?\$ -- (commandline -opc)[2]' -s r -l right -d "Split right-to-left" complete -f -c string -n 'test (count (commandline -opc)) -ge 2; and string match -qr split0\?\$ -- (commandline -opc)[2]' -s n -l no-empty -d "Empty results excluded" complete -f -c string -n "test (count (commandline -opc)) -lt 2" -a collect diff --git a/share/completions/su.fish b/share/completions/su.fish index 410f22c19..226e76d34 100644 --- a/share/completions/su.fish +++ b/share/completions/su.fish @@ -1,12 +1,17 @@ -# Completions for su +function __fish_complete_su_env_whitelist + env | string match -v -e -r '^(?:HOME|SHELL|USER|LOGNAME|PATH)=' | string replace -r '([^=]+)=(.*)' '$1\t$2' +end + +complete -c su -x -a "(__fish_complete_users)" -complete -x -c su -a "(__fish_complete_users)" complete -c su -s l -l login -d "Make login shell" complete -c su -s c -l command -d "Pass command to shell" -xa "(__fish_complete_external_command)" complete -c su -s f -l fast -d "Pass -f to the shell" -complete -c su -s m -l preserve_environment -d "Preserve environment" -complete -c su -s p -d "Preserve environment" -complete -x -c su -s s -l shell -a "(cat /etc/shells)" -complete -c su -l help -d "Display help and exit" -complete -c su -l version -d "Display version and exit" - +complete -c su -s g -l group -x -a "(__fish_complete_groups)" -d "Specify the primary group" +complete -c su -s G -l supp-group -x -a "(__fish_complete_groups)" -d "Specify a supplemental group" +complete -c su -s m -s p -l preserve_environment -d "Preserve environment" +complete -c su -s P -l pty -d "Create pseudo-terminal for the session" +complete -c su -s s -l shell -x -a "(cat /etc/shells)" -d "Run the specified shell" +complete -c su -s w -l whitelist-environment -x -a "(__fish_complete_list , __fish_complete_su_env_whitelist)" -d "Don't reset these environment variables" +complete -c su -s h -l help -d "Display help and exit" +complete -c su -s V -l version -d "Display version and exit" diff --git a/share/completions/svn.fish b/share/completions/svn.fish index 022f3f656..4d16ffc98 100644 --- a/share/completions/svn.fish +++ b/share/completions/svn.fish @@ -320,12 +320,12 @@ _svn_cmpl_ svn:keywords -a Id -d 'A compressed summary of all keywords' # # Completions for the 'relocate' subcommand # -_svn_cmpl_ $relocate -xa '( svn info | string match "*URL:*" | cut -d " " -f 2 ) http:// ftp:// svn+ssh:// svn+ssh://(__fish_print_hostnames)' +_svn_cmpl_ $relocate -xa '( svn info | string match "*URL:*" | string split -f2 " ") http:// ftp:// svn+ssh:// svn+ssh://(__fish_print_hostnames)' # # Completions for the 'switch', 'sw' subcommands # -_svn_cmpl_ $switch -l relocate -d 'Relocate via URL-rewriting' -xa '( svn info | string match "*URL:*" | cut -d " " -f 2 ) http:// ftp:// svn+ssh:// svn+ssh://(__fish_print_hostnames)' +_svn_cmpl_ $switch -l relocate -d 'Relocate via URL-rewriting' -xa '( svn info | string match "*URL:*" | string split -f2 " ") http:// ftp:// svn+ssh:// svn+ssh://(__fish_print_hostnames)' # # Completions for the 'status', 'st' subcommands diff --git a/share/completions/tokei.fish b/share/completions/tokei.fish index 48b82e3b1..8ac4d2e73 100644 --- a/share/completions/tokei.fish +++ b/share/completions/tokei.fish @@ -3,7 +3,7 @@ function __fish_tokei_supported_serializations # Expecting a line like: # tokei 10.0.1 compiled with serialization support: cbor, json, yaml - command tokei --help | grep 'with serialization support' | cut -d : -f 2 | string trim | string split ', ' + command tokei --help | grep 'with serialization support' | string split --fields 2 : | string trim | string split ', ' end complete -c tokei -s f -l files -d 'Print out statistics for individual files' diff --git a/share/completions/unzip.fish b/share/completions/unzip.fish index 30d6f3a26..aa0fb52e9 100644 --- a/share/completions/unzip.fish +++ b/share/completions/unzip.fish @@ -28,7 +28,11 @@ complete -c unzip -s M -d "pipe through `more` pager" if unzip -v 2>/dev/null | string match -eq Debian # the first non-switch argument should be the zipfile - complete -c unzip -n __fish_is_first_token -xa '(__fish_complete_suffix .zip)' + complete -c unzip -n __fish_is_first_token -xa '( + __fish_complete_suffix .zip + __fish_complete_suffix .jar + __fish_complete_suffix .aar + )' # Files thereafter are either files to include or exclude from the operation set -l zipfile @@ -37,6 +41,10 @@ if unzip -v 2>/dev/null | string match -eq Debian else # all tokens should be zip files - complete -c unzip -xa '(__fish_complete_suffix .zip)' + complete -c unzip -xa '( + __fish_complete_suffix .zip + __fish_complete_suffix .jar + __fish_complete_suffix .aar + )' end diff --git a/share/completions/useradd.fish b/share/completions/useradd.fish index 688a0baa0..63a402190 100644 --- a/share/completions/useradd.fish +++ b/share/completions/useradd.fish @@ -7,7 +7,7 @@ complete -c useradd -s c -l comment -d 'A comment about this user' -r complete -c useradd -s d -l home -d 'Home directory for the new user' -x -a '(__fish_complete_directories)' -complete -c useradd -s G -l groups -d 'Supplementary groups' -xa '(__fish_append , (cut -d : -f 1 /etc/group))' +complete -c useradd -s G -l groups -d 'Supplementary groups' -xa '(__fish_append , (string split -f1 : /etc/group))' complete -c useradd -s h -l help -d 'Display help message and exit' complete -c useradd -s m -l create-home -d 'The user\'s home directory will be created if it does not exist' complete -c useradd -s n -d 'A group having the same name as the user being added to the system will be created by default (when -g is not specified)' @@ -18,5 +18,5 @@ complete -c useradd -s u -l uid -d 'The numerical value of the user\'s ID' -r complete -c useradd -s b -l base-dir -d 'The initial path prefix for a new user\'s home directory' -r -a '(__fish_complete_directories)' complete -c useradd -s e -l expiredate -d 'The date on which the user account is disabled' -r complete -c useradd -s f -l inactive -d 'The number of days after a password has expired before the account will be disabled' -r -complete -c useradd -s g -l gid -d 'The group name or ID for a new user\'s initial group' -x -a '(string match -r "^[^#].*" < /etc/group | cut -d : -f 1,3 | string replace -a ":" \n)' +complete -c useradd -s g -l gid -d 'The group name or ID for a new user\'s initial group' -x -a '(string match -r "^[^#].*" < /etc/group | string split -f1,3 ":" | string join ":" | string replace -a ":" \n)' complete -c useradd -s s -l shell -d 'Name of the new user\'s login shell' -x -a '(string match -r "^[^#].*" < /etc/shells)' diff --git a/share/completions/wait.fish b/share/completions/wait.fish new file mode 100644 index 000000000..b97d5b67f --- /dev/null +++ b/share/completions/wait.fish @@ -0,0 +1,3 @@ +complete -c wait -xa '(__fish_complete_job_pids)' +complete -c wait -s n -l any -d 'Return as soon as the first job completes' +complete -c wait -s h -l help -d 'Display help and exit' diff --git a/share/completions/wget.fish b/share/completions/wget.fish index d00f0bbb3..f16c0959d 100644 --- a/share/completions/wget.fish +++ b/share/completions/wget.fish @@ -16,7 +16,7 @@ complete -c wget -o nv -d "Turn off verbose without being completely quiet" complete -c wget -s i -l input-file -d "Read URLs from file" -r complete -c wget -s F -l force-html -d "Force input to be treated as HTML" complete -c wget -s B -l base -d "Prepend string to relative links" -x -complete -c wget -l bind-adress -d "Bind address on local machine" -xa "(__fish_print_addresses; __fish_print_hostnames)" +complete -c wget -l bind-address -d "Bind address on local machine" -xa "(__fish_print_addresses; __fish_print_hostnames)" complete -c wget -s t -l tries -d "Set number of retries to number" -xa "0 1 2 4 8 16 32 64 128" complete -c wget -s O -l output-document -d "Concatenate output to file" -r complete -c wget -l no-clobber -d "Never overwrite files with same name" diff --git a/share/completions/xprop.fish b/share/completions/xprop.fish index 210627c60..a26b67012 100644 --- a/share/completions/xprop.fish +++ b/share/completions/xprop.fish @@ -1,6 +1,6 @@ function __fish_xprop_list_properties # TODO search commandline for a target window ("-root" or "-name foo") - xprop -root | cut -d'(' -f 1 + xprop -root | string split -f1 '(' end complete -c xprop -o help -d "Display help and exit" @@ -19,4 +19,3 @@ complete -c xprop -o set -d "Set property" -x -a " (__fish_xprop_list_properties complete -c xprop -o spy -d "Examine property updates forever" complete -c xprop -o f -d "Set format" complete -c xprop -d Property -x -a "( __fish_xprop_list_properties)" - diff --git a/share/completions/zopfli.fish b/share/completions/zopfli.fish new file mode 100644 index 000000000..1b08d8a40 --- /dev/null +++ b/share/completions/zopfli.fish @@ -0,0 +1,8 @@ +complete -c zopfli -s h -d "Gives this help" +complete -c zopfli -s c -d "Write the result on stdout" +complete -c zopfli -s v -d "Verbose mode" +complete -c zopfli -l i -d "Number of iterations" +complete -c zopfli -l gzip -d "Output to gzip format (default)" +complete -c zopfli -l zlib -d "Output to zlib format" +complete -c zopfli -l deflate -d "Output to deflate format" +complete -c zopfli -l splitlast -d "Left for backwards compatibility" diff --git a/share/completions/zopflipng.fish b/share/completions/zopflipng.fish new file mode 100644 index 000000000..837d3a5ba --- /dev/null +++ b/share/completions/zopflipng.fish @@ -0,0 +1,15 @@ +complete -x -c zopflipng -a "(__fish_complete_suffix .png)" + +complete -c zopflipng -s m -d "Compress more" +complete -c zopflipng -l prefix -d "Add prefix" +complete -c zopflipng -s y -d "Do not ask about overwriting" +complete -c zopflipng -l lossy_transparent -d "Remove colors behind alpha channel 0" +complete -c zopflipng -l lossy_8bit -d "Convert PNG16 to PNG8" +complete -c zopflipng -s d -d "Dry run" +complete -c zopflipng -l always_zopflify -d "For benchmarking the algorithm" +complete -c zopflipng -s q -d "Use quick" +complete -c zopflipng -l iterations -d "Number of iterations" +complete -c zopflipng -l splitting -d "Left for backwards compatibility" +complete -x -c zopflipng -l filters -a "0 1 2 3 4 m e p b" -d "Filter strategies" +complete -c zopflipng -l keepchunks -d "Keep metadata chunks" +complete -c zopflipng -s h -l help -d "Show help" diff --git a/share/config.fish b/share/config.fish index 78e56798e..137d5c37e 100644 --- a/share/config.fish +++ b/share/config.fish @@ -106,15 +106,6 @@ else if not contains -- $__fish_data_dir/completions $fish_complete_path set -a fish_complete_path $__fish_data_dir/completions end -# This cannot be in an autoload-file because `:.fish` is an invalid filename on windows. -function : -d "no-op function" - # for compatibility with sh, bash, and others. - # Often used to insert a comment into a chain of commands without having - # it eat up the remainder of the line, handy in Makefiles. - # This command always succeeds - true -end - # Add a handler for when fish_user_path changes, so we can apply the same changes to PATH function __fish_reconstruct_path -d "Update PATH when fish_user_paths changes" --on-variable fish_user_paths set -l local_path $PATH @@ -163,16 +154,6 @@ end # in UTF-8 (with non-ASCII characters). __fish_set_locale -# "." alias for source; deprecated -function . -d 'Evaluate a file (deprecated, use "source")' --no-scope-shadowing --wraps source - if [ (count $argv) -eq 0 ] && isatty 0 - echo "source: using source via '.' is deprecated, and stdin doesn't work."\n"Did you mean 'source' or './'?" >&2 - return 1 - else - source $argv - end -end - # Upgrade pre-existing abbreviations from the old "key=value" to the new "key value" syntax. # This needs to be in share/config.fish because __fish_config_interactive is called after sourcing # config.fish, which might contain abbr calls. diff --git a/share/functions/__fish_complete_gpg_user_id.fish b/share/functions/__fish_complete_gpg_user_id.fish index 0bc95b171..6abce285f 100644 --- a/share/functions/__fish_complete_gpg_user_id.fish +++ b/share/functions/__fish_complete_gpg_user_id.fish @@ -2,8 +2,7 @@ function __fish_complete_gpg_user_id -d "Complete using gpg user ids" -a __fish_complete_gpg_command # gpg doesn't seem to like it when you use the whole key name as a - # completion, so we skip the part and use it as a - # description. - # It also replaces colons with \x3a - $__fish_complete_gpg_command --list-keys --with-colon | cut -d : -f 10 | sed -ne 's/\\\x3a/:/g' -e 's/\(.*\) <\(.*\)>/\1'\t'\2/p' + # completion, so we skip the part and use it as a description. + # It also replaces \x3a from gpg's output with colons. + $__fish_complete_gpg_command --list-keys --with-colon | string split -a -f 10 : | string replace '\x3a' : | string replace -rf '(.*) <(.*)>' '$1\t$2' end diff --git a/share/functions/__fish_complete_groups.fish b/share/functions/__fish_complete_groups.fish index 523412d5a..021b1bd49 100644 --- a/share/functions/__fish_complete_groups.fish +++ b/share/functions/__fish_complete_groups.fish @@ -1,8 +1,12 @@ function __fish_complete_groups --description "Print a list of local groups, with group members as the description" if command -sq getent - getent group | cut -d ':' -f 1,4 | string replace : \t + getent group | while read -l line + string split -f 1,4 : -- $line | string join \t + end else - cut -d ':' -f 1,4 /etc/group | string replace : \t + while read -l line + string split -f 1,4 : -- $line | string join \t + end &|()"\'])' "$backslash\$1" (commandline -oc)[-1]) + end + + set -q file[1] || return + + # strip -option= from token if present + set file (string replace -r -- '^-[^\s=]*=' '' $file | string collect) + + eval set -l files $file || return # Bail if $file does not tokenize. + + if set -q files[1] && test -f $files[1] + $pager $files + commandline -f repaint + end +end diff --git a/share/functions/__fish_ps.fish b/share/functions/__fish_ps.fish new file mode 100644 index 000000000..8abcbe65d --- /dev/null +++ b/share/functions/__fish_ps.fish @@ -0,0 +1,8 @@ +function __fish_ps + switch (realpath (command -v ps) | string match -r '[^/]+$') + case busybox + command ps $argv + case '*' + command ps axc $argv + end +end diff --git a/share/functions/__fish_shared_key_bindings.fish b/share/functions/__fish_shared_key_bindings.fish index f39b3d0b3..7a4522cab 100644 --- a/share/functions/__fish_shared_key_bindings.fish +++ b/share/functions/__fish_shared_key_bindings.fish @@ -84,6 +84,7 @@ function __fish_shared_key_bindings -d "Bindings shared between emacs and vi mod bind --preset $argv \e. history-token-search-backward bind --preset $argv \el __fish_list_current_token + bind --preset $argv \eo __fish_preview_current_file bind --preset $argv \ew __fish_whatis_current_token # ncurses > 6.0 sends a "delete scrollback" sequence along with clear. # This string replace removes it. diff --git a/share/functions/__fish_systemctl.fish b/share/functions/__fish_systemctl.fish index a08aa99f3..6d8a05203 100644 --- a/share/functions/__fish_systemctl.fish +++ b/share/functions/__fish_systemctl.fish @@ -74,12 +74,11 @@ function __fish_systemctl --description 'Call systemctl with some options from t # Output looks like # systemd-tmpfiles-clean.timer [more whitespace] loaded active waiting Daily Cleanup[...] # Use the last part as the description. - # Note that in some cases this prints a "●" or "*" (with C locale) marker at the beginning of the line. We have to remove it. - systemctl --no-legend --no-pager --all list-units $passflags | string trim -c ' *●' | string replace -r "(?: +(\S+)){4}" \t'$1' + systemctl --full --no-legend --no-pager --plain --all list-units $passflags | string replace -r "(?: +(\S+)){4}" \t'$1' # We need this for disabled/static units. Also instance units without an active instance. # Output looks like # systemd-tmpfiles-clean.timer static # Just use the state as the description, since we won't get it here. # This is an issue for units that appear in both. - systemctl --no-legend --no-pager --all list-unit-files $passflags | string trim -c ' *●' | string replace -r "(?: +(\S+)){1}" \t'$1' + systemctl --full --no-legend --no-pager --plain --all list-unit-files $passflags | string replace -r "(?: +(\S+)){1}" \t'$1' end diff --git a/share/functions/__fish_systemctl_services.fish b/share/functions/__fish_systemctl_services.fish index 57251a20e..d422e67ac 100644 --- a/share/functions/__fish_systemctl_services.fish +++ b/share/functions/__fish_systemctl_services.fish @@ -1,13 +1,13 @@ function __fish_systemctl_services if type -q systemctl if __fish_contains_opt user - systemctl --user list-unit-files --no-legend --type=service 2>/dev/null $argv | cut -f 1 -d ' ' - systemctl --user list-units --state=loaded --no-legend --type=service 2>/dev/null | cut -f 1 -d ' ' + systemctl --user list-unit-files --full --no-legend --no-pager --plain --type=service 2>/dev/null $argv | string split -f 1 ' ' + systemctl --user list-units --state=loaded --full --no-legend --no-pager --plain --type=service 2>/dev/null | string split -f 1 ' ' else # list-unit-files will also show disabled units - systemctl list-unit-files --no-legend --type=service 2>/dev/null $argv | cut -f 1 -d ' ' + systemctl list-unit-files --full --no-legend --no-pager --plain --type=service 2>/dev/null $argv | string split -f 1 ' ' # list-units will not show disabled units but will show instances (like wpa_supplicant@wlan0.service) - systemctl list-units --state=loaded --no-legend --type=service 2>/dev/null | cut -f 1 -d ' ' + systemctl list-units --state=loaded --full --no-legend --no-pager --plain --type=service 2>/dev/null | string split -f 1 ' ' end end end diff --git a/share/functions/alias.fish b/share/functions/alias.fish index 43c3a0ffc..320ddb0e2 100644 --- a/share/functions/alias.fish +++ b/share/functions/alias.fish @@ -17,7 +17,7 @@ function alias --description 'Creates a function wrapping a command' if not set -q argv[1] # Print the known aliases. for func in (functions -n) - set -l output (functions $func | string match -r -- "^function .* --description 'alias (.*)'") + set -l output (functions $func | string match -r -- "^function .* --description (?:'alias (.*)'|alias\\\\ (.*))\$") if set -q output[2] set output (string replace -r -- '^'$func'[= ]' '' $output[2]) echo alias $func (string escape -- $output[1]) diff --git a/share/functions/fish_clipboard_copy.fish b/share/functions/fish_clipboard_copy.fish index 47fa8264a..6bb60a473 100644 --- a/share/functions/fish_clipboard_copy.fish +++ b/share/functions/fish_clipboard_copy.fish @@ -3,14 +3,14 @@ function fish_clipboard_copy set -l cmdline (commandline --current-selection) test -n "$cmdline"; or set cmdline (commandline) if type -q pbcopy - printf '%s\n' $cmdline | pbcopy + printf '%s' $cmdline | pbcopy else if set -q WAYLAND_DISPLAY; and type -q wl-copy - printf '%s\n' $cmdline | wl-copy + printf '%s' $cmdline | wl-copy else if type -q xsel # Silence error so no error message shows up # if e.g. X isn't running. - printf '%s\n' $cmdline | xsel --clipboard 2>/dev/null + printf '%s' $cmdline | xsel --clipboard 2>/dev/null else if type -q xclip - printf '%s\n' $cmdline | xclip -selection clipboard 2>/dev/null + printf '%s' $cmdline | xclip -selection clipboard 2>/dev/null end end diff --git a/share/functions/fish_git_prompt.fish b/share/functions/fish_git_prompt.fish index 2e6d97694..6222a8671 100644 --- a/share/functions/fish_git_prompt.fish +++ b/share/functions/fish_git_prompt.fish @@ -414,7 +414,7 @@ function fish_git_prompt --description "Prompt function for Git" end if set -q __fish_git_prompt_showstashstate - and test -r $git_dir/refs/stash + and test -r $git_dir/logs/refs/stash set s $___fish_git_prompt_char_stashstate end diff --git a/share/functions/fish_hg_prompt.fish b/share/functions/fish_hg_prompt.fish index 567416a03..113567ef6 100644 --- a/share/functions/fish_hg_prompt.fish +++ b/share/functions/fish_hg_prompt.fish @@ -28,7 +28,7 @@ function fish_hg_prompt --description 'Write out the hg prompt' end set -l root (fish_print_hg_root) - or return 0 + or return 1 # Read branch and bookmark set -l branch (cat $root/branch 2>/dev/null; or echo default) @@ -62,7 +62,7 @@ function fish_hg_prompt --description 'Write out the hg prompt' # Add a character for each file status if we have one # HACK: To allow this to work both with and without '?' globs - set -l q '?' + set -l dq '?' switch $line case 'A ' set -a hg_statuses added diff --git a/share/functions/fish_vi_key_bindings.fish b/share/functions/fish_vi_key_bindings.fish index ddfaf6e86..f83bfe0a3 100644 --- a/share/functions/fish_vi_key_bindings.fish +++ b/share/functions/fish_vi_key_bindings.fish @@ -187,7 +187,7 @@ function fish_vi_key_bindings --description 'vi-like key bindings for fish' bind -s --preset -m insert ci backward-jump-till and repeat-jump-reverse and begin-selection repeat-jump kill-selection end-selection repaint-mode bind -s --preset -m insert ca backward-jump and repeat-jump-reverse and begin-selection repeat-jump kill-selection end-selection repaint-mode - bind -s --preset '~' capitalize-word + bind -s --preset '~' togglecase-char forward-char bind -s --preset gu downcase-word bind -s --preset gU upcase-word @@ -291,6 +291,7 @@ function fish_vi_key_bindings --description 'vi-like key bindings for fish' bind -s --preset -M visual -m default X kill-whole-line end-selection repaint-mode bind -s --preset -M visual -m default y kill-selection yank end-selection repaint-mode bind -s --preset -M visual -m default '"*y' "commandline -s | xsel -p; commandline -f end-selection repaint-mode" + bind -s --preset -M visual -m default '~' togglecase-selection end-selection repaint-mode bind -s --preset -M visual -m default \cc end-selection repaint-mode bind -s --preset -M visual -m default \e end-selection repaint-mode diff --git a/share/functions/help.fish b/share/functions/help.fish index b4985d10e..41d7864ee 100644 --- a/share/functions/help.fish +++ b/share/functions/help.fish @@ -162,7 +162,7 @@ function help --description 'Show help for the fish shell' end else # Go to the web. Only include one dot in the version string - set -l version_string (echo $version| cut -d . -f 1,2) + set -l version_string (string split . -f 1,2 -- $version | string join .) set page_url https://fishshell.com/docs/$version_string/$fish_help_page # We don't need a trampoline for a remote URL. set need_trampoline diff --git a/share/tools/create_manpage_completions.py b/share/tools/create_manpage_completions.py index 23cef2a95..0a1e5e9ad 100755 --- a/share/tools/create_manpage_completions.py +++ b/share/tools/create_manpage_completions.py @@ -16,7 +16,8 @@ Redistributions in binary form must reproduce the above copyright notice, this l THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. """ -import string, sys, re, os.path, bz2, gzip, traceback, getopt, errno, codecs +import string, sys, re, os.path, bz2, gzip, traceback, errno, codecs +import argparse from deroff import Deroffer lzma_available = True @@ -48,8 +49,8 @@ diagnostic_indent = 0 VERY_VERBOSE, BRIEF_VERBOSE, NOT_VERBOSE = 2, 1, 0 # Pick some reasonable default values for settings -global VERBOSITY, WRITE_TO_STDOUT, DEROFF_ONLY -VERBOSITY, WRITE_TO_STDOUT, DEROFF_ONLY = NOT_VERBOSE, False, False +global VERBOSITY, WRITE_TO_STDOUT, DEROFF_ONLY, KEEP_FILES +VERBOSITY, WRITE_TO_STDOUT, DEROFF_ONLY, KEEP_FILES = NOT_VERBOSE, False, False, False def add_diagnostic(dgn, msg_verbosity=VERY_VERBOSE): @@ -994,101 +995,88 @@ def get_paths_from_man_locations(): return result -def usage(script_name): - print( - "Usage: {0} [-v, --verbose] [-s, --stdout] [-d, --directory] [-p, --progress]" - " [-c, --cleanup-in] [-z] files...".format(script_name) - ) - print( - """Command options are: - -h, --help\t\tShow this help message - -v, --verbose [0, 1, 2]\tShow debugging output to stderr. Larger is more verbose. - -s, --stdout\tWrite all completions to stdout (trumps the --directory option) - -d, --directory [dir]\tWrite all completions to the given directory, instead of to ~/.local/share/fish/generated_completions - -m, --manpath\tProcess all man1 and man8 files available in the manpath (as determined by manpath) - -p, --progress\tShow progress - -c, --cleanup-in\tRemove all .fish files out of a given directory. - -z\t\tParse using only Deroff parser. - """ - ) - - if __name__ == "__main__": script_name = sys.argv[0] - try: - opts, file_paths = getopt.gnu_getopt( - sys.argv[1:], - "v:sd:hmpc:z", - [ - "verbose=", - "stdout", - "directory=", - "cleanup-in=", - "help", - "manpath", - "progress", - ], - ) - except getopt.GetoptError as err: - print(err.msg) # will print something like "option -a not recognized" - usage(script_name) - sys.exit(2) + parser = argparse.ArgumentParser( + description="create_manpage_completions: Generate fish-shell completions from manpages" + ) + parser.add_argument( + "-c", + "--cleanup-in", + type=str, + help="Directories to clean up in", + action="append", + ) + parser.add_argument( + "-d", "--directory", type=str, help="The directory to save the completions in", + ) + parser.add_argument( + "-k", + "--keep", + help="Whether to keep files in the target directory", + action="store_true", + ) + parser.add_argument( + "-m", "--manpath", help="Whether to use manpath", action="store_true", + ) + parser.add_argument( + "-p", "--progress", help="Whether to show progress", action="store_true", + ) + parser.add_argument( + "-s", "--stdout", help="Write the completions to stdout", action="store_true", + ) + parser.add_argument( + "-v", + "--verbose", + type=int, + choices=[0, 1, 2], + help="The level of debug output to show", + ) + parser.add_argument( + "-z", "--deroff-only", help="Whether to just deroff", action="store_true", + ) + parser.add_argument("file_paths", type=str, nargs="*") + + args = parser.parse_args() + + if args.verbose: + VERBOSITY = args.verbose + if args.stdout: + WRITE_TO_STDOUT = True + if args.deroff_only: + DEROFF_ONLY = True + if args.keep: + KEEP_FILES = True + if args.manpath: + # Fetch all man1 and man8 files from the manpath or man.conf + args.file_paths.extend(get_paths_from_man_locations()) # Directories within which we will clean up autogenerated completions # This script originally wrote completions into ~/.config/fish/completions # Now it writes them into a separate directory - cleanup_directories = [] - - use_manpath, show_progress, custom_dir = False, False, False - output_directory = "" - for opt, value in opts: - if opt in ("-v", "--verbose"): - VERBOSITY = int(value) - elif opt in ("-s", "--stdout"): - WRITE_TO_STDOUT = True - elif opt in ("-d", "--directory"): - output_directory = value - elif opt in ("-h", "--help"): - usage(script_name) - sys.exit(0) - elif opt in ("-m", "--manpath"): - use_manpath = True - elif opt in ("-p", "--progress"): - show_progress = True - elif opt in ("-c", "--cleanup-in"): - cleanup_directories.append(value) - elif opt in ("-z",): - DEROFF_ONLY = True - else: - assert False, "unhandled option" - - if use_manpath: - # Fetch all man1 and man8 files from the manpath or man.conf - file_paths.extend(get_paths_from_man_locations()) - - if cleanup_directories: - for cleanup_dir in cleanup_directories: + if args.cleanup_in: + for cleanup_dir in args.cleanup_in: cleanup_autogenerated_completions_in_directory(cleanup_dir) - if not file_paths: + if not args.file_paths: print("No paths specified") sys.exit(0) - if not WRITE_TO_STDOUT and not output_directory: + if not args.stdout and not args.directory: # Default to ~/.local/share/fish/generated_completions/ # Create it if it doesn't exist xdg_data_home = os.getenv("XDG_DATA_HOME", "~/.local/share") - output_directory = os.path.expanduser( + args.directory = os.path.expanduser( xdg_data_home + "/fish/generated_completions/" ) try: - os.makedirs(output_directory) + os.makedirs(args.directory) except OSError as e: if e.errno != errno.EEXIST: raise - if not WRITE_TO_STDOUT: + if not args.stdout and not args.keep: # Remove old generated files - cleanup_autogenerated_completions_in_directory(output_directory) + cleanup_autogenerated_completions_in_directory(args.directory) - parse_and_output_man_pages(file_paths, output_directory, show_progress) + parse_and_output_man_pages(args.file_paths, args.directory, args.progress) diff --git a/src/autoload.cpp b/src/autoload.cpp index 11c51c92e..7661c15f2 100644 --- a/src/autoload.cpp +++ b/src/autoload.cpp @@ -141,7 +141,7 @@ maybe_t autoload_file_cache_t::check(const wcstring &cmd, b autoload_t::autoload_t(wcstring env_var_name) : env_var_name_(std::move(env_var_name)), cache_(make_unique()) {} -autoload_t::autoload_t(autoload_t &&) = default; +autoload_t::autoload_t(autoload_t &&) noexcept = default; autoload_t::~autoload_t() = default; void autoload_t::invalidate_cache() { diff --git a/src/builtin.cpp b/src/builtin.cpp index 832bedcff..3914f59a5 100644 --- a/src/builtin.cpp +++ b/src/builtin.cpp @@ -137,7 +137,6 @@ int parse_help_only_cmd_opts(struct help_only_cmd_opts_t &opts, int *optind, int } default: { DIE("unexpected retval from wgetopt_long"); - break; } } } @@ -317,20 +316,14 @@ static int builtin_breakpoint(parser_t &parser, io_streams_t &streams, wchar_t * int builtin_true(parser_t &parser, io_streams_t &streams, wchar_t **argv) { UNUSED(parser); UNUSED(streams); - if (argv[1] != nullptr) { - streams.err.append_format(BUILTIN_ERR_ARG_COUNT1, argv[0], 0, builtin_count_args(argv) - 1); - return STATUS_INVALID_ARGS; - } + UNUSED(argv); return STATUS_CMD_OK; } int builtin_false(parser_t &parser, io_streams_t &streams, wchar_t **argv) { UNUSED(parser); UNUSED(streams); - if (argv[1] != nullptr) { - streams.err.append_format(BUILTIN_ERR_ARG_COUNT1, argv[0], 0, builtin_count_args(argv) - 1); - return STATUS_INVALID_ARGS; - } + UNUSED(argv); return STATUS_CMD_ERROR; } @@ -342,6 +335,8 @@ int builtin_false(parser_t &parser, io_streams_t &streams, wchar_t **argv) { // Functions that are bound to builtin_generic are handled directly by the parser. // NOTE: These must be kept in sorted order! static const builtin_data_t builtin_datas[] = { + {L".", &builtin_source, N_(L"Evaluate contents of file")}, + {L":", &builtin_true, N_(L"Return a successful result")}, {L"[", &builtin_test, N_(L"Test a condition")}, {L"and", &builtin_generic, N_(L"Execute command if previous command succeeded")}, {L"argparse", &builtin_argparse, N_(L"Parse options in fish script")}, diff --git a/src/builtin_argparse.cpp b/src/builtin_argparse.cpp index ca1ad096f..3e16d135c 100644 --- a/src/builtin_argparse.cpp +++ b/src/builtin_argparse.cpp @@ -51,7 +51,7 @@ struct argparse_cmd_opts_t { size_t min_args = 0; size_t max_args = SIZE_MAX; wchar_t implicit_int_flag = L'\0'; - wcstring name = L""; + wcstring name; wcstring_list_t raw_exclusive_flags; wcstring_list_t argv; std::unordered_map options; @@ -398,7 +398,6 @@ static int parse_cmd_opts(argparse_cmd_opts_t &opts, int *optind, //!OCLINT(hig } default: { DIE("unexpected retval from wgetopt_long"); - break; } } } diff --git a/src/builtin_bind.cpp b/src/builtin_bind.cpp index 78522a4cc..3c7c088e1 100644 --- a/src/builtin_bind.cpp +++ b/src/builtin_bind.cpp @@ -404,7 +404,6 @@ int parse_cmd_opts(bind_cmd_opts_t &opts, int *optind, //!OCLINT(high ncss meth } default: { DIE("unexpected retval from wgetopt_long"); - break; } } } diff --git a/src/builtin_block.cpp b/src/builtin_block.cpp index f1f566553..fcf2dd3a2 100644 --- a/src/builtin_block.cpp +++ b/src/builtin_block.cpp @@ -61,7 +61,6 @@ static int parse_cmd_opts(block_cmd_opts_t &opts, int *optind, //!OCLINT(high n } default: { DIE("unexpected retval from wgetopt_long"); - break; } } } @@ -125,7 +124,6 @@ int builtin_block(parser_t &parser, io_streams_t &streams, wchar_t **argv) { } default: { DIE("unexpected scope"); - break; } } if (block) { diff --git a/src/builtin_builtin.cpp b/src/builtin_builtin.cpp index f04a9c32f..7e94ee2a1 100644 --- a/src/builtin_builtin.cpp +++ b/src/builtin_builtin.cpp @@ -54,7 +54,6 @@ static int parse_cmd_opts(builtin_cmd_opts_t &opts, int *optind, int argc, wchar } default: { DIE("unexpected retval from wgetopt_long"); - break; } } } diff --git a/src/builtin_command.cpp b/src/builtin_command.cpp index d63bdbefa..6de2347ac 100644 --- a/src/builtin_command.cpp +++ b/src/builtin_command.cpp @@ -63,7 +63,6 @@ static int parse_cmd_opts(command_cmd_opts_t &opts, int *optind, int argc, wchar } default: { DIE("unexpected retval from wgetopt_long"); - break; } } } diff --git a/src/builtin_commandline.cpp b/src/builtin_commandline.cpp index e8cf67f7f..bfa4211b2 100644 --- a/src/builtin_commandline.cpp +++ b/src/builtin_commandline.cpp @@ -74,7 +74,6 @@ static void replace_part(const wchar_t *begin, const wchar_t *end, const wchar_t } default: { DIE("unexpected append_mode"); - break; } } out.append(end); @@ -275,7 +274,6 @@ int builtin_commandline(parser_t &parser, io_streams_t &streams, wchar_t **argv) } default: { DIE("unexpected retval from wgetopt_long"); - break; } } } @@ -414,7 +412,6 @@ int builtin_commandline(parser_t &parser, io_streams_t &streams, wchar_t **argv) } default: { DIE("unexpected buffer_part"); - break; } } diff --git a/src/builtin_complete.cpp b/src/builtin_complete.cpp index 6bdc11592..38d3873bb 100644 --- a/src/builtin_complete.cpp +++ b/src/builtin_complete.cpp @@ -261,7 +261,6 @@ int builtin_complete(parser_t &parser, io_streams_t &streams, wchar_t **argv) { } default: { DIE("unexpected retval from wgetopt_long"); - break; } } } diff --git a/src/builtin_contains.cpp b/src/builtin_contains.cpp index a9e018859..91ddb3c14 100644 --- a/src/builtin_contains.cpp +++ b/src/builtin_contains.cpp @@ -48,7 +48,6 @@ static int parse_cmd_opts(contains_cmd_opts_t &opts, int *optind, int argc, wcha } default: { DIE("unexpected retval from wgetopt_long"); - break; } } } diff --git a/src/builtin_echo.cpp b/src/builtin_echo.cpp index d2bd798f1..bac2adf85 100644 --- a/src/builtin_echo.cpp +++ b/src/builtin_echo.cpp @@ -56,7 +56,6 @@ static int parse_cmd_opts(echo_cmd_opts_t &opts, int *optind, int argc, wchar_t } default: { DIE("unexpected retval from wgetopt_long"); - break; } } } diff --git a/src/builtin_exit.cpp b/src/builtin_exit.cpp index 920107116..8073dee53 100644 --- a/src/builtin_exit.cpp +++ b/src/builtin_exit.cpp @@ -49,7 +49,6 @@ static int parse_cmd_opts(exit_cmd_opts_t &opts, int *optind, //!OCLINT(high nc } default: { DIE("unexpected retval from wgetopt_long"); - break; } } } diff --git a/src/builtin_function.cpp b/src/builtin_function.cpp index 31a22ce25..065603e4f 100644 --- a/src/builtin_function.cpp +++ b/src/builtin_function.cpp @@ -29,7 +29,7 @@ struct function_cmd_opts_t { bool print_help = false; bool shadow_scope = true; - wcstring description = L""; + wcstring description; std::vector events; wcstring_list_t named_arguments; wcstring_list_t inherit_vars; @@ -164,7 +164,6 @@ static int parse_cmd_opts(function_cmd_opts_t &opts, int *optind, //!OCLINT(hig } default: { DIE("unexpected retval from wgetopt_long"); - break; } } } diff --git a/src/builtin_function.h b/src/builtin_function.h index be69f8485..9499a9a9f 100644 --- a/src/builtin_function.h +++ b/src/builtin_function.h @@ -9,5 +9,6 @@ class parser_t; struct io_streams_t; int builtin_function(parser_t &parser, io_streams_t &streams, const wcstring_list_t &c_args, - const parsed_source_ref_t &source, tnode_t body); + const parsed_source_ref_t &source, + tnode_t func_node); #endif diff --git a/src/builtin_functions.cpp b/src/builtin_functions.cpp index bfd840e41..aaec72d0b 100644 --- a/src/builtin_functions.cpp +++ b/src/builtin_functions.cpp @@ -120,7 +120,6 @@ static int parse_cmd_opts(functions_cmd_opts_t &opts, int *optind, //!OCLINT(hi } default: { DIE("unexpected retval from wgetopt_long"); - break; } } } @@ -194,7 +193,6 @@ static wcstring functions_def(const wcstring &name) { case event_type_t::any: default: { DIE("unexpected next->type"); - break; } } } diff --git a/src/builtin_history.cpp b/src/builtin_history.cpp index 6facf2bbb..0009f58d2 100644 --- a/src/builtin_history.cpp +++ b/src/builtin_history.cpp @@ -191,7 +191,6 @@ static int parse_cmd_opts(history_cmd_opts_t &opts, int *optind, //!OCLINT(high } default: { DIE("unexpected retval from wgetopt_long"); - break; } } } @@ -306,7 +305,6 @@ int builtin_history(parser_t &parser, io_streams_t &streams, wchar_t **argv) { } case HIST_UNDEF: { DIE("Unexpected HIST_UNDEF seen"); - break; } } diff --git a/src/builtin_jobs.cpp b/src/builtin_jobs.cpp index e8065666e..6b0aa84fb 100644 --- a/src/builtin_jobs.cpp +++ b/src/builtin_jobs.cpp @@ -105,7 +105,6 @@ static void builtin_jobs_print(const job_t *j, int mode, int header, io_streams_ } default: { DIE("unexpected mode"); - break; } } } @@ -165,7 +164,6 @@ int builtin_jobs(parser_t &parser, io_streams_t &streams, wchar_t **argv) { } default: { DIE("unexpected retval from wgetopt_long"); - break; } } } diff --git a/src/builtin_math.cpp b/src/builtin_math.cpp index be998b1e1..ac0caee6b 100644 --- a/src/builtin_math.cpp +++ b/src/builtin_math.cpp @@ -76,7 +76,6 @@ static int parse_cmd_opts(math_cmd_opts_t &opts, int *optind, //!OCLINT(high nc } default: { DIE("unexpected retval from wgetopt_long"); - break; } } } diff --git a/src/builtin_printf.cpp b/src/builtin_printf.cpp index bda9d974b..662d24a58 100644 --- a/src/builtin_printf.cpp +++ b/src/builtin_printf.cpp @@ -488,7 +488,7 @@ void builtin_printf_state_t::print_direc(const wchar_t *start, size_t length, wc switch (conversion) { case L'd': case L'i': { - intmax_t arg = string_to_scalar_type(argument, this); + auto arg = string_to_scalar_type(argument, this); if (!have_field_width) { if (!have_precision) this->append_format_output(fmt.c_str(), arg); @@ -506,7 +506,7 @@ void builtin_printf_state_t::print_direc(const wchar_t *start, size_t length, wc case L'u': case L'x': case L'X': { - uintmax_t arg = string_to_scalar_type(argument, this); + auto arg = string_to_scalar_type(argument, this); if (!have_field_width) { if (!have_precision) this->append_format_output(fmt.c_str(), arg); @@ -528,7 +528,7 @@ void builtin_printf_state_t::print_direc(const wchar_t *start, size_t length, wc case L'F': case L'g': case L'G': { - long double arg = string_to_scalar_type(argument, this); + auto arg = string_to_scalar_type(argument, this); if (!have_field_width) { if (!have_precision) { this->append_format_output(fmt.c_str(), arg); @@ -570,7 +570,6 @@ void builtin_printf_state_t::print_direc(const wchar_t *start, size_t length, wc } default: { DIE("unexpected opt"); - break; } } } @@ -579,7 +578,7 @@ void builtin_printf_state_t::print_direc(const wchar_t *start, size_t length, wc static inline void modify_allowed_format_specifiers(bool ok[UCHAR_MAX + 1], const char *str, bool flag) { for (const char *c = str; *c != '\0'; c++) { - unsigned char idx = static_cast(*c); + auto idx = static_cast(*c); ok[idx] = flag; } } @@ -654,7 +653,7 @@ int builtin_printf_state_t::print_formatted(const wchar_t *format, int argc, wch ++f; ++direc_length; if (argc > 0) { - intmax_t width = string_to_scalar_type(*argv, this); + auto width = string_to_scalar_type(*argv, this); if (INT_MIN <= width && width <= INT_MAX) field_width = static_cast(width); else @@ -679,7 +678,7 @@ int builtin_printf_state_t::print_formatted(const wchar_t *format, int argc, wch ++f; ++direc_length; if (argc > 0) { - intmax_t prec = string_to_scalar_type(*argv, this); + auto prec = string_to_scalar_type(*argv, this); if (prec < 0) { // A negative precision is taken as if the precision were omitted, // so -1 is safe here even if prec < INT_MIN. diff --git a/src/builtin_pwd.cpp b/src/builtin_pwd.cpp index 6216230f5..e3619881f 100644 --- a/src/builtin_pwd.cpp +++ b/src/builtin_pwd.cpp @@ -43,7 +43,6 @@ int builtin_pwd(parser_t &parser, io_streams_t &streams, wchar_t **argv) { } default: { DIE("unexpected retval from wgetopt_long"); - break; } } } diff --git a/src/builtin_read.cpp b/src/builtin_read.cpp index 93d67409b..52d30679f 100644 --- a/src/builtin_read.cpp +++ b/src/builtin_read.cpp @@ -186,7 +186,6 @@ static int parse_cmd_opts(read_cmd_opts_t &opts, int *optind, //!OCLINT(high nc } default: { DIE("unexpected retval from wgetopt_long"); - break; } } } @@ -204,6 +203,7 @@ static int read_interactive(parser_t &parser, wcstring &buff, int nchars, bool s // Don't keep history reader_push(parser, L""); + reader_get_history()->resolve_pending(); reader_set_left_prompt(prompt); reader_set_right_prompt(right_prompt); diff --git a/src/builtin_return.cpp b/src/builtin_return.cpp index 767f503cb..8c0fb302b 100644 --- a/src/builtin_return.cpp +++ b/src/builtin_return.cpp @@ -48,7 +48,6 @@ static int parse_cmd_opts(return_cmd_opts_t &opts, int *optind, //!OCLINT(high } default: { DIE("unexpected retval from wgetopt_long"); - break; } } } diff --git a/src/builtin_set.cpp b/src/builtin_set.cpp index 9e20f4a96..2f7632ef1 100644 --- a/src/builtin_set.cpp +++ b/src/builtin_set.cpp @@ -163,7 +163,6 @@ static int parse_cmd_opts(set_cmd_opts_t &opts, int *optind, //!OCLINT(high ncs } default: { DIE("unexpected retval from wgetopt_long"); - break; } } } @@ -336,7 +335,6 @@ static void handle_env_return(int retval, const wchar_t *cmd, const wchar_t *key } default: { DIE("unexpected vars.set() ret val"); - break; } } } @@ -572,7 +570,6 @@ static void show_scope(const wchar_t *var_name, int scope, io_streams_t &streams } default: { DIE("invalid scope"); - break; } } diff --git a/src/builtin_set_color.cpp b/src/builtin_set_color.cpp index a2fcd3ed1..66acaf470 100644 --- a/src/builtin_set_color.cpp +++ b/src/builtin_set_color.cpp @@ -60,7 +60,7 @@ static const struct woption long_options[] = {{L"background", required_argument, {L"print-colors", no_argument, nullptr, 'c'}, {nullptr, 0, nullptr, 0}}; -#if __APPLE__ +#ifdef __APPLE__ static char sitm_esc[] = "\x1B[3m"; static char ritm_esc[] = "\x1B[23m"; static char dim_esc[] = "\x1B[2m"; @@ -73,7 +73,7 @@ int builtin_set_color(parser_t &parser, io_streams_t &streams, wchar_t **argv) { // Hack in missing italics and dim capabilities omitted from MacOS xterm-256color terminfo // Helps Terminal.app/iTerm -#if __APPLE__ +#ifdef __APPLE__ const auto term_prog = parser.vars().get(L"TERM_PROGRAM"); if (!term_prog.missing_or_empty() && (term_prog->as_string() == L"Apple_Terminal" || term_prog->as_string() == L"iTerm.app")) { @@ -145,7 +145,6 @@ int builtin_set_color(parser_t &parser, io_streams_t &streams, wchar_t **argv) { } default: { DIE("unexpected retval from wgetopt_long"); - break; } } } diff --git a/src/builtin_status.cpp b/src/builtin_status.cpp index 012a18f54..47e567789 100644 --- a/src/builtin_status.cpp +++ b/src/builtin_status.cpp @@ -259,7 +259,6 @@ static int parse_cmd_opts(status_cmd_opts_t &opts, int *optind, //!OCLINT(high } default: { DIE("unexpected retval from wgetopt_long"); - break; } } } @@ -350,7 +349,7 @@ int builtin_status(parser_t &parser, io_streams_t &streams, wchar_t **argv) { streams.err.append_format(BUILTIN_ERR_ARG_COUNT2, cmd, subcmd_str, 1, args.size()); return STATUS_INVALID_ARGS; } - const auto *metadata = features_t::metadata_for(args.front().c_str()); + auto metadata = features_t::metadata_for(args.front().c_str()); if (!metadata) { retval = TEST_FEATURE_NOT_RECOGNIZED; } else { diff --git a/src/builtin_string.cpp b/src/builtin_string.cpp index a570c81be..09ca8b970 100644 --- a/src/builtin_string.cpp +++ b/src/builtin_string.cpp @@ -1,5 +1,5 @@ // Implementation of the string builtin. -#include "config.h" +#include "config.h" // IWYU pragma: keep #define PCRE2_CODE_UNIT_WIDTH WCHAR_T_BITS #ifdef _WIN32 @@ -133,7 +133,7 @@ class arg_iterator_t { // This is used by the string subcommands to communicate with the option parser which flags are // valid and get the result of parsing the command for flags. -typedef struct { //!OCLINT(too many fields) +using options_t = struct options_t { //!OCLINT(too many fields) bool all_valid = false; bool chars_valid = false; bool count_valid = false; @@ -155,6 +155,8 @@ typedef struct { //!OCLINT(too many fields) bool style_valid = false; bool no_empty_valid = false; bool no_trim_newlines_valid = false; + bool fields_valid = false; + bool allow_empty_valid = false; bool all = false; bool entire = false; @@ -170,6 +172,7 @@ typedef struct { //!OCLINT(too many fields) bool right = false; bool no_empty = false; bool no_trim_newlines = false; + bool allow_empty = false; long count = 0; long length = 0; @@ -177,12 +180,14 @@ typedef struct { //!OCLINT(too many fields) long start = 0; long end = 0; + std::vector fields; + const wchar_t *chars_to_trim = L" \f\n\r\t\v"; const wchar_t *arg1 = nullptr; const wchar_t *arg2 = nullptr; escape_string_style_t escape_style = STRING_STYLE_SCRIPT; -} options_t; +}; /// This handles the `--style=xxx` flag. static int handle_flag_1(wchar_t **argv, parser_t &parser, io_streams_t &streams, @@ -227,6 +232,9 @@ static int handle_flag_a(wchar_t **argv, parser_t &parser, io_streams_t &streams if (opts->all_valid) { opts->all = true; return STATUS_CMD_OK; + } else if (opts->allow_empty_valid) { + opts->allow_empty = true; + return STATUS_CMD_OK; } string_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]); return STATUS_INVALID_ARGS; @@ -267,6 +275,51 @@ static int handle_flag_f(wchar_t **argv, parser_t &parser, io_streams_t &streams if (opts->filter_valid) { opts->filter = true; return STATUS_CMD_OK; + } else if (opts->fields_valid) { + for (const wcstring &s : split_string(w.woptarg, L',')) { + wcstring_list_t range = split_string(s, L'-'); + if (range.size() == 2) { + int begin = fish_wcstoi(range.at(0).c_str()); + if (begin <= 0 || begin == INT_MIN || errno == ERANGE) { + string_error(streams, _(L"%ls: Invalid range value for field '%ls'\n"), argv[0], + w.woptarg); + return STATUS_INVALID_ARGS; + } else if (errno) { + string_error(streams, BUILTIN_ERR_NOT_NUMBER, argv[0], w.woptarg); + return STATUS_INVALID_ARGS; + } + int end = fish_wcstoi(range.at(1).c_str()); + if (end <= 0 || end == INT_MIN || errno == ERANGE) { + string_error(streams, _(L"%ls: Invalid range value for field '%ls'\n"), argv[0], + w.woptarg); + return STATUS_INVALID_ARGS; + } else if (errno) { + string_error(streams, BUILTIN_ERR_NOT_NUMBER, argv[0], w.woptarg); + return STATUS_INVALID_ARGS; + } + if (begin <= end) { + for (int i = begin; i <= end; i++) { + opts->fields.push_back(i); + } + } else { + for (int i = begin; i >= end; i--) { + opts->fields.push_back(i); + } + } + } else { + int field = fish_wcstoi(s.c_str()); + if (field <= 0 || field == INT_MIN || errno == ERANGE) { + string_error(streams, _(L"%ls: Invalid fields value '%ls'\n"), argv[0], + w.woptarg); + return STATUS_INVALID_ARGS; + } else if (errno) { + string_error(streams, BUILTIN_ERR_NOT_NUMBER, argv[0], w.woptarg); + return STATUS_INVALID_ARGS; + } + opts->fields.push_back(field); + } + } + return STATUS_CMD_OK; } string_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]); return STATUS_INVALID_ARGS; @@ -423,6 +476,8 @@ static wcstring construct_short_opts(options_t *opts) { //!OCLINT(high npath co if (opts->end_valid) short_opts.append(L"e:"); if (opts->no_empty_valid) short_opts.append(L"n"); if (opts->no_trim_newlines_valid) short_opts.append(L"N"); + if (opts->fields_valid) short_opts.append(L"f:"); + if (opts->allow_empty_valid) short_opts.append(L"a"); return short_opts; } @@ -450,6 +505,8 @@ static const struct woption long_options[] = {{L"all", no_argument, nullptr, 'a' {L"start", required_argument, nullptr, 's'}, {L"style", required_argument, nullptr, 1}, {L"no-trim-newlines", no_argument, nullptr, 'N'}, + {L"fields", required_argument, nullptr, 'f'}, + {L"allow-empty", no_argument, nullptr, 'a'}, {nullptr, 0, nullptr, 0}}; static const std::unordered_map flag_to_function = { @@ -821,7 +878,7 @@ class pcre2_matcher_t : public string_matcher_t { if (opts.invert_match) return true; // Report any additional matches. - for (auto *ovector = pcre2_get_ovector_pointer(regex.match); opts.all; total_matched++) { + for (auto ovector = pcre2_get_ovector_pointer(regex.match); opts.all; total_matched++) { uint32_t options = 0; PCRE2_SIZE offset = ovector[1]; // start at end of previous match @@ -1002,7 +1059,7 @@ bool regex_replacer_t::replace_matches(const wcstring &arg) { (opts.all ? PCRE2_SUBSTITUTE_GLOBAL : 0); size_t arglen = arg.length(); PCRE2_SIZE bufsize = (arglen == 0) ? 16 : 2 * arglen; - wchar_t *output = static_cast(malloc(sizeof(wchar_t) * bufsize)); + auto output = static_cast(malloc(sizeof(wchar_t) * bufsize)); int pcre2_rc; PCRE2_SIZE outlen = bufsize; @@ -1021,8 +1078,7 @@ bool regex_replacer_t::replace_matches(const wcstring &arg) { done = true; } else { bufsize = outlen; - wchar_t *new_output = - static_cast(realloc(output, sizeof(wchar_t) * bufsize)); + auto new_output = static_cast(realloc(output, sizeof(wchar_t) * bufsize)); if (new_output) output = new_output; } } @@ -1077,22 +1133,33 @@ static int string_replace(parser_t &parser, io_streams_t &streams, int argc, wch static int string_split_maybe0(parser_t &parser, io_streams_t &streams, int argc, wchar_t **argv, bool is_split0) { + wchar_t *cmd = argv[0]; options_t opts; opts.quiet_valid = true; opts.right_valid = true; opts.max_valid = true; opts.max = LONG_MAX; opts.no_empty_valid = true; + opts.fields_valid = true; + opts.allow_empty_valid = true; int optind; int retval = parse_opts(&opts, &optind, is_split0 ? 0 : 1, argc, argv, parser, streams); if (retval != STATUS_CMD_OK) return retval; + if (opts.fields.size() < 1 && opts.allow_empty) { + streams.err.append_format(BUILTIN_ERR_COMBO2, cmd, + _(L"--allow-empty is only valid with --fields")); + return STATUS_INVALID_ARGS; + } + const wcstring sep = is_split0 ? wcstring(1, L'\0') : wcstring(opts.arg1); - wcstring_list_t splits; + std::vector all_splits; + size_t split_count = 0; size_t arg_count = 0; arg_iterator_t aiter(argv, optind, streams, !is_split0); while (const wcstring *arg = aiter.nextstr()) { + wcstring_list_t splits; if (opts.right) { split_about(arg->rbegin(), arg->rend(), sep.rbegin(), sep.rend(), &splits, opts.max, opts.no_empty); @@ -1100,32 +1167,52 @@ static int string_split_maybe0(parser_t &parser, io_streams_t &streams, int argc split_about(arg->begin(), arg->end(), sep.begin(), sep.end(), &splits, opts.max, opts.no_empty); } + all_splits.push_back(splits); + split_count += splits.size(); arg_count++; } - // If we are from the right, split_about gave us reversed strings, in reversed order! - if (opts.right) { - for (auto &split : splits) { - std::reverse(split.begin(), split.end()); + for (auto &splits : all_splits) { + // If we are from the right, split_about gave us reversed strings, in reversed order! + if (opts.right) { + for (auto &split : splits) { + std::reverse(split.begin(), split.end()); + } + std::reverse(splits.begin(), splits.end()); } - std::reverse(splits.begin(), splits.end()); - } - const size_t split_count = splits.size(); - if (!opts.quiet) { - if (is_split0 && !splits.empty()) { - // split0 ignores a trailing \0, so a\0b\0 is two elements. - // In contrast to split, where a\nb\n is three - "a", "b" and "". - // - // Remove the last element if it is empty. - if (splits.back().empty()) splits.pop_back(); - } - auto &buff = streams.out.buffer(); - for (const wcstring &split : splits) { - buff.append(split, separation_type_t::explicitly); + if (!opts.quiet) { + if (is_split0 && !splits.empty()) { + // split0 ignores a trailing \0, so a\0b\0 is two elements. + // In contrast to split, where a\nb\n is three - "a", "b" and "". + // + // Remove the last element if it is empty. + if (splits.back().empty()) splits.pop_back(); + } + auto &buff = streams.out.buffer(); + if (opts.fields.size() > 0) { + // Print nothing and return error if any of the supplied + // fields do not exist, unless `--allow-empty` is used. + if (!opts.allow_empty) { + for (const auto &field : opts.fields) { + // field indexing starts from 1 + if (field - 1 >= (long)splits.size()) { + return STATUS_CMD_ERROR; + } + } + } + for (const auto &field : opts.fields) { + if (field - 1 < (long)splits.size()) { + buff.append(splits.at(field - 1), separation_type_t::explicitly); + } + } + } else { + for (const wcstring &split : splits) { + buff.append(split, separation_type_t::explicitly); + } + } } } - // We split something if we have more split values than args. return split_count > arg_count ? STATUS_CMD_OK : STATUS_CMD_ERROR; } @@ -1244,7 +1331,7 @@ static int string_sub(parser_t &parser, io_streams_t &streams, int argc, wchar_t pos = static_cast(opts.start - 1); } else if (opts.start < 0) { assert(opts.start != LONG_MIN); // checked above - size_type n = static_cast(-opts.start); + auto n = static_cast(-opts.start); pos = n > s->length() ? 0 : s->length() - n; } diff --git a/src/builtin_test.cpp b/src/builtin_test.cpp index c2007f0dd..7f15c2efd 100644 --- a/src/builtin_test.cpp +++ b/src/builtin_test.cpp @@ -517,7 +517,6 @@ unique_ptr test_parser::parse_expression(unsigned int start, unsigne switch (argc) { case 0: { DIE("argc should not be zero"); // should have been caught by the above test - break; } case 1: { return error(L"Missing argument at index %u", start + 1); diff --git a/src/builtin_ulimit.cpp b/src/builtin_ulimit.cpp index 3fda263d1..df9621538 100644 --- a/src/builtin_ulimit.cpp +++ b/src/builtin_ulimit.cpp @@ -253,7 +253,6 @@ int builtin_ulimit(parser_t &parser, io_streams_t &streams, wchar_t **argv) { } default: { DIE("unexpected retval from wgetopt_long"); - break; } } } diff --git a/src/builtin_wait.cpp b/src/builtin_wait.cpp index 861c92788..c02edae96 100644 --- a/src/builtin_wait.cpp +++ b/src/builtin_wait.cpp @@ -178,9 +178,11 @@ int builtin_wait(parser_t &parser, io_streams_t &streams, wchar_t **argv) { const wchar_t *cmd = argv[0]; int argc = builtin_count_args(argv); bool any_flag = false; // flag for -n option + bool print_help = false; - static const wchar_t *const short_options = L":n"; + static const wchar_t *const short_options = L":nh"; static const struct woption long_options[] = {{L"any", no_argument, nullptr, 'n'}, + {L"help", no_argument, nullptr, 'h'}, {nullptr, 0, nullptr, 0}}; int opt; @@ -190,6 +192,9 @@ int builtin_wait(parser_t &parser, io_streams_t &streams, wchar_t **argv) { case 'n': any_flag = true; break; + case 'h': + print_help = true; + break; case ':': { builtin_missing_argument(parser, streams, cmd, argv[w.woptind - 1]); return STATUS_INVALID_ARGS; @@ -200,11 +205,15 @@ int builtin_wait(parser_t &parser, io_streams_t &streams, wchar_t **argv) { } default: { DIE("unexpected retval from wgetopt_long"); - break; } } } + if (print_help) { + builtin_print_help(parser, streams, cmd); + return STATUS_CMD_OK; + } + if (w.woptind == argc) { // no jobs specified retval = wait_for_backgrounds(parser, any_flag); diff --git a/src/color.cpp b/src/color.cpp index bfb2a52c8..aea715738 100644 --- a/src/color.cpp +++ b/src/color.cpp @@ -87,15 +87,15 @@ static int parse_hex_digit(wchar_t x) { } static unsigned long squared_difference(long p1, long p2) { - unsigned long diff = static_cast(labs(p1 - p2)); + auto diff = static_cast(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 = static_cast(-1); - unsigned char best_index = static_cast(-1); + auto best_distance = static_cast(-1); + auto best_index = static_cast(-1); for (size_t idx = 0; idx < color_count; idx++) { uint32_t color = colors[idx]; long test_r = (color >> 16) & 0xFF, test_g = (color >> 8) & 0xFF, @@ -173,9 +173,9 @@ wcstring_list_t rgb_color_t::named_color_names() { const size_t count = sizeof named_colors / sizeof *named_colors; wcstring_list_t result; result.reserve(1 + count); - for (size_t i = 0; i < count; i++) { - if (!named_colors[i].hidden) { - result.push_back(named_colors[i].name); + for (const auto &named_color : named_colors) { + if (!named_color.hidden) { + result.push_back(named_color.name); } } // "normal" isn't really a color and does not have a color palette index or diff --git a/src/common.cpp b/src/common.cpp index 0f33f7364..d39012065 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -42,7 +42,7 @@ #ifdef __FreeBSD__ #include -#elif __APPLE__ +#elif defined(__APPLE__) #include #endif @@ -361,7 +361,7 @@ char *wcs2str(const wchar_t *in, size_t len) { } // Here we probably allocate a buffer probably much larger than necessary. - char *out = static_cast(malloc(MAX_UTF8_BYTES * len + 1)); + auto out = static_cast(malloc(MAX_UTF8_BYTES * len + 1)); assert(out); // Instead of returning the return value of wcs2str_internal, return `out` directly. // This eliminates false warnings in coverity about resource leaks. @@ -1210,7 +1210,7 @@ wcstring debug_escape(const wcstring &in) { wcstring result; result.reserve(in.size()); for (wchar_t wc : in) { - uint32_t c = static_cast(wc); + auto c = static_cast(wc); if (c <= 127 && isprint(c)) { result.push_back(wc); continue; @@ -2148,7 +2148,7 @@ void assert_is_background_thread(const char *who) { } void assert_is_locked(void *vmutex, const char *who, const char *caller) { - std::mutex *mutex = static_cast(vmutex); + auto mutex = static_cast(vmutex); // Note that std::mutex.try_lock() is allowed to return false when the mutex isn't // actually locked; fortunately we are checking the opposite so we're safe. @@ -2219,13 +2219,13 @@ bool valid_func_name(const wcstring &str) { std::string get_executable_path(const char *argv0) { char buff[PATH_MAX]; -#if __APPLE__ +#ifdef __APPLE__ // On OS X use it's proprietary API to get the path to the executable. // This is basically grabbing exec_path after argc, argv, envp, ...: for us // https://opensource.apple.com/source/adv_cmds/adv_cmds-163/ps/print.c uint32_t buffSize = sizeof buff; if (_NSGetExecutablePath(buff, &buffSize) == 0) return std::string(buff); -#elif __FreeBSD__ +#elif defined(__FreeBSD__) // FreeBSD does not have /proc by default, but it can be mounted as procfs via the // Linux compatibility layer. Per sysctl(3), passing in a process ID of -1 returns // the value for the current process. diff --git a/src/complete.cpp b/src/complete.cpp index 65e98b5de..1b7c1288a 100644 --- a/src/complete.cpp +++ b/src/complete.cpp @@ -89,7 +89,7 @@ static const wcstring &C_(const wcstring &s) { return s; } /// If option is non-empty, it specifies a switch for the command. If \c comp is also not empty, it /// contains a list of non-switch arguments that may only follow directly after the specified /// switch. -typedef struct complete_entry_opt { +using complete_entry_opt_t = struct complete_entry_opt { // Text of the option (like 'foo'). wcstring option; // Type of the option: args-oly, short, single_long, or double_long. @@ -119,8 +119,7 @@ typedef struct complete_entry_opt { } DIE("unreachable"); } - -} complete_entry_opt_t; +}; /// Last value used in the order field of completion_entry_t. static std::atomic k_complete_order{0}; @@ -132,7 +131,6 @@ class completion_entry_t { /// List of all options. option_list_t options; - public: /// Command string. const wcstring cmd; /// True if command is a path. @@ -422,7 +420,7 @@ bool completer_t::condition_test(const wcstring &condition) { ASSERT_IS_MAIN_THREAD(); bool test_res; - condition_cache_t::iterator cached_entry = condition_cache.find(condition); + auto cached_entry = condition_cache.find(condition); if (cached_entry == condition_cache.end()) { // Compute new value and reinsert it. test_res = @@ -476,7 +474,7 @@ void complete_add(const wchar_t *cmd, bool cmd_is_path, const wcstring &option, /// option strings. Returns true if it is now empty and should be deleted, false if it's not empty. /// Must be called while locked. bool completion_entry_t::remove_option(const wcstring &option, complete_option_type_t type) { - option_list_t::iterator iter = this->options.begin(); + auto iter = this->options.begin(); while (iter != this->options.end()) { if (iter->option == option && iter->type == type) { iter = this->options.erase(iter); @@ -493,10 +491,10 @@ void complete_remove(const wcstring &cmd, bool cmd_is_path, const wcstring &opti auto completion_set = s_completion_set.acquire(); completion_entry_t tmp_entry(cmd, cmd_is_path); - completion_entry_set_t::iterator iter = completion_set->find(tmp_entry); + auto iter = completion_set->find(tmp_entry); if (iter != completion_set->end()) { // const_cast: See SET_ELEMENTS_ARE_IMMUTABLE. - completion_entry_t &entry = const_cast(*iter); + auto &entry = const_cast(*iter); bool delete_it = entry.remove_option(option, type); if (delete_it) { @@ -1788,7 +1786,7 @@ bool complete_remove_wrapper(const wcstring &command, const wcstring &target_to_ auto locked_map = wrapper_map.acquire(); wrapper_map_t &wraps = *locked_map; bool result = false; - wrapper_map_t::iterator current_targets_iter = wraps.find(command); + auto current_targets_iter = wraps.find(command); if (current_targets_iter != wraps.end()) { wcstring_list_t *targets = ¤t_targets_iter->second; auto where = std::find(targets->begin(), targets->end(), target_to_remove); diff --git a/src/env.cpp b/src/env.cpp index 7ed5426b2..d7c526403 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -93,6 +93,7 @@ static const electric_var_t electric_variables[] = { {L"_", electric_var_t::freadonly}, {L"fish_private_mode", electric_var_t::freadonly}, {L"umask", electric_var_t::fcomputed}, + {L"fish_kill_signal", electric_var_t::freadonly | electric_var_t::fcomputed}, }; const electric_var_t *electric_var_t::for_name(const wcstring &name) { @@ -106,7 +107,7 @@ const electric_var_t *electric_var_t::for_name(const wcstring &name) { /// Check if a variable may not be set using the set command. static bool is_read_only(const wcstring &key) { - if (const auto *ev = electric_var_t::for_name(key)) { + if (auto ev = electric_var_t::for_name(key)) { return ev->readonly(); } // Hack. @@ -685,6 +686,9 @@ maybe_t env_scoped_impl_t::try_get_computed(const wcstring &key) cons } else if (key == L"status") { const auto &js = perproc_data().statuses; return env_var_t(L"status", to_string(js.status)); + } else if (key == L"fish_kill_signal") { + const auto &js = perproc_data().statuses; + return env_var_t(L"fish_kill_signal", to_string(js.kill_signal)); } else if (key == L"umask") { // note umask() is an absurd API: you call it to set the value and it returns the old // value. Thus we have to call it twice, to reset the value. The env_lock protects diff --git a/src/env.h b/src/env.h index 4b0b6421f..273ce9c5c 100644 --- a/src/env.h +++ b/src/env.h @@ -63,6 +63,10 @@ struct statuses_t { /// Status of the last job to exit. int status{0}; + /// Signal from the most recent process in the last job that was terminated by a signal. + /// 0 if all processes exited normally. + int kill_signal{0}; + /// Pipestatus value. std::vector pipestatus{}; diff --git a/src/env_universal_common.cpp b/src/env_universal_common.cpp index 507f2301f..e8ccf229a 100644 --- a/src/env_universal_common.cpp +++ b/src/env_universal_common.cpp @@ -48,8 +48,8 @@ #include "wcstringutil.h" #include "wutil.h" -#if __APPLE__ -#define FISH_NOTIFYD_AVAILABLE 1 +#ifdef __APPLE__ +#define FISH_NOTIFYD_AVAILABLE #include #endif @@ -253,13 +253,13 @@ env_universal_t::env_universal_t(wcstring path) : narrow_vars_path(wcs2string(path)), explicit_vars_path(std::move(path)) {} maybe_t env_universal_t::get(const wcstring &name) const { - var_table_t::const_iterator where = vars.find(name); + auto where = vars.find(name); if (where != vars.end()) return where->second; return none(); } maybe_t env_universal_t::get_flags(const wcstring &name) const { - var_table_t::const_iterator where = vars.find(name); + auto where = vars.find(name); if (where != vars.end()) { return where->second.get_flags(); } @@ -360,7 +360,7 @@ void env_universal_t::generate_callbacks_and_update_exports(const var_table_t &n void env_universal_t::acquire_variables(var_table_t &vars_to_acquire) { // Copy modified values from existing vars to vars_to_acquire. for (const auto &key : this->modified) { - var_table_t::iterator src_iter = this->vars.find(key); + auto src_iter = this->vars.find(key); if (src_iter == this->vars.end()) { /* The value has been deleted. */ vars_to_acquire.erase(key); @@ -1178,7 +1178,7 @@ class universal_notifier_shmem_poller_t : public universal_notifier_t { } #else // this class isn't valid on this system public: - universal_notifier_shmem_poller_t() { + [[noreturn]] universal_notifier_shmem_poller_t() { DIE("universal_notifier_shmem_poller_t cannot be used on this system"); } #endif @@ -1186,7 +1186,7 @@ class universal_notifier_shmem_poller_t : public universal_notifier_t { /// A notifyd-based notifier. Very straightforward. class universal_notifier_notifyd_t : public universal_notifier_t { -#if FISH_NOTIFYD_AVAILABLE +#ifdef FISH_NOTIFYD_AVAILABLE int notify_fd; int token; std::string name; @@ -1257,7 +1257,7 @@ class universal_notifier_notifyd_t : public universal_notifier_t { } #else // this class isn't valid on this system public: - universal_notifier_notifyd_t() { + [[noreturn]] universal_notifier_notifyd_t() { DIE("universal_notifier_notifyd_t cannot be used on this system"); } #endif @@ -1288,7 +1288,7 @@ class universal_notifier_named_pipe_t : public universal_notifier_t { void make_pipe(const wchar_t *test_path); - void drain_excessive_data() { + void drain_excessive_data() const { // The pipe seems to have data on it, that won't go away. Read a big chunk out of it. We // don't read until it's exhausted, because if someone were to pipe say /dev/null, that // would cause us to hang! @@ -1435,7 +1435,7 @@ class universal_notifier_named_pipe_t : public universal_notifier_t { }; universal_notifier_t::notifier_strategy_t universal_notifier_t::resolve_default_strategy() { -#if FISH_NOTIFYD_AVAILABLE +#ifdef FISH_NOTIFYD_AVAILABLE return strategy_notifyd; #elif defined(__CYGWIN__) return strategy_shmem_polling; diff --git a/src/exec.cpp b/src/exec.cpp index b575e7aa5..30c831f00 100644 --- a/src/exec.cpp +++ b/src/exec.cpp @@ -87,14 +87,15 @@ pgroup_provenance_t get_pgroup_provenance(const shared_ptr &j, /// This function is executed by the child process created by a call to fork(). It should be called /// after \c child_setup_process. It calls execve to replace the fish process image with the command /// specified in \c p. It never returns. Called in a forked child! Do not allocate memory, etc. +[[noreturn]] static void safe_launch_process(process_t *p, const char *actual_cmd, const char *const *cargv, const char *const *cenvv) { UNUSED(p); int err; // This function never returns, so we take certain liberties with constness. - char *const *envv = const_cast(cenvv); - char *const *argv = const_cast(cargv); + const auto envv = const_cast(cenvv); + const auto argv = const_cast(cargv); execve(actual_cmd, argv, envv); err = errno; @@ -132,6 +133,7 @@ static void safe_launch_process(process_t *p, const char *actual_cmd, const char /// This function is similar to launch_process, except it is not called after a fork (i.e. it only /// calls exec) and therefore it can allocate memory. +[[noreturn]] static void launch_process_nofork(env_stack_t &vars, process_t *p) { ASSERT_IS_MAIN_THREAD(); ASSERT_IS_NOT_FORKED_CHILD(); @@ -187,7 +189,7 @@ static void internal_exec(env_stack_t &vars, job_t *j, const io_chain_t &block_i // child_setup_process makes sure signals are properly set up. dup2_list_t redirs = dup2_list_t::resolve_chain(all_ios); - if (child_setup_process(INVALID_PID, false, redirs) == 0) { + if (child_setup_process(INVALID_PID, *j, false, redirs) == 0) { // Decrement SHLVL as we're removing ourselves from the shell "stack". auto shlvl_var = vars.get(L"SHLVL", ENV_GLOBAL | ENV_EXPORT); wcstring shlvl_str = L"0"; @@ -325,6 +327,16 @@ static void run_internal_process_or_short_circuit(parser_t &parser, const std::s } } +bool blocked_signals_for_job(const job_t &job, sigset_t *sigmask) { + // Block some signals in background jobs for which job control is turned off (#6828). + if (!job.is_foreground() && !job.wants_job_control()) { + sigaddset(sigmask, SIGINT); + sigaddset(sigmask, SIGQUIT); + return true; + } + return false; +} + /// Call fork() as part of executing a process \p p in a job \j. Execute \p child_action in the /// context of the child. Returns true if fork succeeded, false if fork failed. static bool fork_child_for_process(const std::shared_ptr &job, process_t *p, @@ -337,7 +349,7 @@ static bool fork_child_for_process(const std::shared_ptr &job, process_t maybe_t new_termowner{}; p->pid = getpid(); child_set_group(job.get(), p); - child_setup_process(job->should_claim_terminal() ? job->pgid : INVALID_PID, true, dup2s); + child_setup_process(job->should_claim_terminal() ? job->pgid : INVALID_PID, *job, true, dup2s); child_action(); DIE("Child process returned control to fork_child lambda!"); } @@ -855,7 +867,6 @@ static bool exec_process_in_job(parser_t &parser, process_t *p, const std::share // We should have handled exec up above. DIE("process_type_t::exec process found in pipeline, where it should never be. " "Aborting."); - break; } } return true; @@ -1062,8 +1073,7 @@ static void populate_subshell_output(wcstring_list_t *lst, const io_buffer_t &bu const char *cursor = begin; while (cursor < end) { // Look for the next separator. - const char *stop = - static_cast(std::memchr(cursor, '\n', end - cursor)); + auto stop = static_cast(std::memchr(cursor, '\n', end - cursor)); const bool hit_separator = (stop != nullptr); if (!hit_separator) { // If it's not found, just use the end. diff --git a/src/exec.h b/src/exec.h index 640b65bbf..6c52be8db 100644 --- a/src/exec.h +++ b/src/exec.h @@ -39,4 +39,8 @@ void exec_close(int fd); /// assigned. It's factored out because the logic has subtleties, and this centralizes it. pgroup_provenance_t get_pgroup_provenance(const std::shared_ptr &j, const job_lineage_t &lineage); + +/// Add signals that should be masked for external processes in this job. +bool blocked_signals_for_job(const job_t &job, sigset_t *sigmask); + #endif diff --git a/src/expand.cpp b/src/expand.cpp index 5f17eb7f7..942461dff 100644 --- a/src/expand.cpp +++ b/src/expand.cpp @@ -16,7 +16,7 @@ #ifdef SunOS #include #endif -#if __APPLE__ +#ifdef __APPLE__ #include // Required to build with old SDK versions // proc.h needs to be included *after* time.h, this comment stops clang-format from reordering. #include @@ -615,7 +615,6 @@ static expand_result_t expand_cmdsubst(wcstring input, parser_t &parser, } default: { DIE("unhandled parse_ret value"); - break; } } @@ -649,7 +648,12 @@ static expand_result_t expand_cmdsubst(wcstring input, parser_t &parser, bad_pos = parse_slice(slice_begin, &slice_end, slice_idx, sub_res.size()); if (bad_pos != 0) { - append_syntax_error(errors, slice_begin - in + bad_pos, L"Invalid index value"); + if (tail_begin[bad_pos] == L'0') { + append_syntax_error(errors, slice_begin - in + bad_pos, + L"array indices start at 1, not 0."); + } else { + append_syntax_error(errors, slice_begin - in + bad_pos, L"Invalid index value"); + } return expand_result_t::make_error(STATUS_EXPAND_ERROR); } @@ -923,7 +927,7 @@ expand_result_t expander_t::stage_variables(wcstring input, completion_list_t *o // We accept incomplete strings here, since complete uses expand_string to expand incomplete // strings from the commandline. wcstring next; - unescape_string(std::move(input), &next, UNESCAPE_SPECIAL | UNESCAPE_INCOMPLETE); + unescape_string(input, &next, UNESCAPE_SPECIAL | UNESCAPE_INCOMPLETE); if (flags & expand_flag::skip_variables) { for (auto &i : next) { diff --git a/src/expand.h b/src/expand.h index 423b0559c..0e09b61e6 100644 --- a/src/expand.h +++ b/src/expand.h @@ -189,7 +189,7 @@ wcstring expand_escape_variable(const env_var_t &var); /// Convert a string value to a human readable form, i.e. escape things, handle arrays, etc. /// Suitable for pretty-printing. -wcstring expand_escape_string(const wcstring &str); +wcstring expand_escape_string(const wcstring &el); /// Perform tilde expansion and nothing else on the specified string, which is modified in place. /// diff --git a/src/fallback.cpp b/src/fallback.cpp index 6b7ff3d62..c44e55b37 100644 --- a/src/fallback.cpp +++ b/src/fallback.cpp @@ -63,12 +63,13 @@ int fish_mkstemp_cloexec(char *name_template) { if (&mkostemp != nullptr) { return mkostemp(name_template, O_CLOEXEC); } -#endif +#else int result_fd = mkstemp(name_template); if (result_fd != -1) { fcntl(result_fd, F_SETFD, FD_CLOEXEC); } return result_fd; +#endif } /// Fallback implementations of wcsdup and wcscasecmp. On systems where these are not needed (e.g. @@ -77,7 +78,7 @@ int fish_mkstemp_cloexec(char *name_template) { // cppcheck-suppress unusedFunction [[gnu::unused]] static wchar_t *wcsdup_fallback(const wchar_t *in) { size_t len = std::wcslen(in); - wchar_t *out = static_cast(malloc(sizeof(wchar_t) * (len + 1))); + auto out = static_cast(malloc(sizeof(wchar_t) * (len + 1))); if (out == nullptr) { return nullptr; } @@ -112,7 +113,7 @@ int fish_mkstemp_cloexec(char *name_template) { return wcsncasecmp_fallback(a + 1, b + 1, count - 1); } -#if __APPLE__ +#ifdef __APPLE__ #if __DARWIN_C_LEVEL >= 200809L // Note parens avoid the macro expansion. wchar_t *wcsdup_use_weak(const wchar_t *a) { @@ -162,7 +163,7 @@ int wcsncasecmp(const wchar_t *a, const wchar_t *b, size_t n) { #ifndef HAVE_WCSNDUP wchar_t *wcsndup(const wchar_t *in, size_t c) { - wchar_t *res = static_cast(malloc(sizeof(wchar_t) * (c + 1))); + auto res = static_cast(malloc(sizeof(wchar_t) * (c + 1))); if (res == nullptr) { return nullptr; } diff --git a/src/fallback.h b/src/fallback.h index 04f56158c..f9788755b 100644 --- a/src/fallback.h +++ b/src/fallback.h @@ -73,7 +73,7 @@ char *tparm_solaris_kludge(char *str, long p1 = 0, long p2 = 0, long p3 = 0, lon // these functions only exist on 10.7+. // // On other platforms, use what's detected at build time. -#if __APPLE__ +#ifdef __APPLE__ // Avoid warnings about unknown `clang::weak_import` attribute (e.g. GCC 8.2.0 on macOS 10.10) #if __DARWIN_C_LEVEL >= 200809L && __clang__ && __has_attribute(weak_import) // We have to explicitly redeclare these as weak, since we are forced to set the MIN_REQUIRED diff --git a/src/fish.cpp b/src/fish.cpp index 55d56de07..7849724bc 100644 --- a/src/fish.cpp +++ b/src/fish.cpp @@ -236,7 +236,7 @@ static void source_config_in_directory(parser_t &parser, const wcstring &dir) { } /// Parse init files. exec_path is the path of fish executable as determined by argv[0]. -static int read_init(parser_t &parser, const struct config_paths_t &paths) { +static void read_init(parser_t &parser, const struct config_paths_t &paths) { source_config_in_directory(parser, paths.data); source_config_in_directory(parser, paths.sysconf); @@ -247,8 +247,6 @@ static int read_init(parser_t &parser, const struct config_paths_t &paths) { if (path_get_config(config_dir)) { source_config_in_directory(parser, config_dir); } - - return 1; } int run_command_list(parser_t &parser, std::vector *cmds, const io_chain_t &io) { @@ -338,17 +336,16 @@ static int fish_parse_opt(int argc, char **argv, fish_cmd_opts_t *opts) { auto cats = get_flog_categories(); // Compute width of longest name. int name_width = 0; - for (const auto *cat : cats) { + for (auto cat : cats) { name_width = std::max(name_width, static_cast(wcslen(cat->name))); } // A little extra space. name_width += 2; - for (const auto *cat : cats) { + for (auto cat : cats) { // Negating the name width left-justifies. printf("%*ls %ls\n", -name_width, cat->name, _(cat->description)); } exit(0); - break; } case 'p': { s_profiling_output_filename = optarg; @@ -362,7 +359,6 @@ static int fish_parse_opt(int argc, char **argv, fish_cmd_opts_t *opts) { case 'v': { std::fwprintf(stdout, _(L"%s, version %s\n"), PACKAGE_NAME, get_fish_version()); exit(0); - break; } case 'D': { char *end; @@ -383,7 +379,6 @@ static int fish_parse_opt(int argc, char **argv, fish_cmd_opts_t *opts) { default: { // We assume getopt_long() has already emitted a diagnostic msg. exit(1); - break; } } } @@ -473,48 +468,47 @@ int main(int argc, char **argv) { parser_t &parser = parser_t::principal_parser(); - if (read_init(parser, paths)) { - // Stomp the exit status of any initialization commands (issue #635). - parser.set_last_statuses(statuses_t::just(STATUS_CMD_OK)); + read_init(parser, paths); + // Stomp the exit status of any initialization commands (issue #635). + parser.set_last_statuses(statuses_t::just(STATUS_CMD_OK)); - // Run post-config commands specified as arguments, if any. - if (!opts.postconfig_cmds.empty()) { - res = run_command_list(parser, &opts.postconfig_cmds, {}); + // Run post-config commands specified as arguments, if any. + if (!opts.postconfig_cmds.empty()) { + res = run_command_list(parser, &opts.postconfig_cmds, {}); + } + + if (!opts.batch_cmds.empty()) { + // Run the commands specified as arguments, if any. + if (get_login()) { + // Do something nasty to support OpenSUSE assuming we're bash. This may modify cmds. + fish_xdm_login_hack_hack_hack_hack(&opts.batch_cmds, argc - my_optind, + argv + my_optind); } - - if (!opts.batch_cmds.empty()) { - // Run the commands specified as arguments, if any. - if (get_login()) { - // Do something nasty to support OpenSUSE assuming we're bash. This may modify cmds. - fish_xdm_login_hack_hack_hack_hack(&opts.batch_cmds, argc - my_optind, - argv + my_optind); - } - res = run_command_list(parser, &opts.batch_cmds, {}); - reader_set_end_loop(false); - } else if (my_optind == argc) { - // Implicitly interactive mode. - res = reader_read(parser, STDIN_FILENO, {}); + res = run_command_list(parser, &opts.batch_cmds, {}); + reader_set_end_loop(false); + } else if (my_optind == argc) { + // Implicitly interactive mode. + res = reader_read(parser, STDIN_FILENO, {}); + } else { + const char *file = *(argv + (my_optind++)); + autoclose_fd_t fd(open_cloexec(file, O_RDONLY)); + if (!fd.valid()) { + perror(file); } else { - const char *file = *(argv + (my_optind++)); - autoclose_fd_t fd(open_cloexec(file, O_RDONLY)); - if (!fd.valid()) { - perror(file); - } else { - wcstring_list_t list; - for (char **ptr = argv + my_optind; *ptr; ptr++) { - list.push_back(str2wcstring(*ptr)); - } - parser.vars().set(L"argv", ENV_DEFAULT, std::move(list)); + wcstring_list_t list; + for (char **ptr = argv + my_optind; *ptr; ptr++) { + list.push_back(str2wcstring(*ptr)); + } + parser.vars().set(L"argv", ENV_DEFAULT, std::move(list)); - auto &ld = parser.libdata(); - wcstring rel_filename = str2wcstring(file); - scoped_push filename_push{&ld.current_filename, - intern(rel_filename.c_str())}; - res = reader_read(parser, fd.fd(), {}); - if (res) { - FLOGF(warning, _(L"Error while reading file %ls\n"), - ld.current_filename ? ld.current_filename : _(L"Standard input")); - } + auto &ld = parser.libdata(); + wcstring rel_filename = str2wcstring(file); + scoped_push filename_push{&ld.current_filename, + intern(rel_filename.c_str())}; + res = reader_read(parser, fd.fd(), {}); + if (res) { + FLOGF(warning, _(L"Error while reading file %ls\n"), + ld.current_filename ? ld.current_filename : _(L"Standard input")); } } } diff --git a/src/fish_indent.cpp b/src/fish_indent.cpp index 52c21b3fc..fa4b79ae6 100644 --- a/src/fish_indent.cpp +++ b/src/fish_indent.cpp @@ -47,6 +47,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA #include "tnode.h" #include "wutil.h" // IWYU pragma: keep +// The number of spaces per indent isn't supposed to be configurable. +// See discussion at https://github.com/fish-shell/fish-shell/pull/6790 #define SPACES_PER_INDENT 4 // An indent_t represents an abstract indent depth. 2 means we are in a doubly-nested block, etc. @@ -571,12 +573,10 @@ int main(int argc, char *argv[]) { case 'h': { print_help("fish_indent", 1); exit(0); - break; } case 'v': { std::fwprintf(stderr, _(L"%ls, version %s\n"), program_name, get_fish_version()); exit(0); - break; } case 'w': { output_type = output_type_file; @@ -632,7 +632,6 @@ int main(int argc, char *argv[]) { default: { // We assume getopt_long() has already emitted a diagnostic msg. exit(1); - break; } } } @@ -706,7 +705,6 @@ int main(int argc, char *argv[]) { } case output_type_pygments_csv: { DIE("pygments_csv should have been handled above"); - break; } } diff --git a/src/fish_key_reader.cpp b/src/fish_key_reader.cpp index 39a9e88a5..f3bef3ecc 100644 --- a/src/fish_key_reader.cpp +++ b/src/fish_key_reader.cpp @@ -242,6 +242,7 @@ static void process_input(bool continuous_mode) { } /// Setup our environment (e.g., tty modes), process key strokes, then reset the environment. +[[noreturn]] static void setup_and_process_keys(bool continuous_mode) { set_interactive_session(session_interactivity_t::implied); set_main_thread(); diff --git a/src/fish_test_helper.cpp b/src/fish_test_helper.cpp index bb56debfa..afb0b94ed 100644 --- a/src/fish_test_helper.cpp +++ b/src/fish_test_helper.cpp @@ -69,6 +69,28 @@ static void print_fds() { fputc('\n', stdout); } +static void print_blocked_signals() { + sigset_t sigs; + sigemptyset(&sigs); + if (sigprocmask(SIG_SETMASK, nullptr, &sigs)) { + perror("sigprocmask"); + exit(EXIT_FAILURE); + } + // There is no obviously portable way to get the maximum number of signals. + // Here we limit it to 64 because strsignal on Linux returns "Unknown signal" for anything above. + for (int sig=1; sig < 65; sig++) { + if (sigismember(&sigs, sig)) { + if (const char *s = strsignal(sig)) { + fprintf(stderr, "%s", s); + if (strchr(s, ':') == nullptr) { + fprintf(stderr, ": %d", sig); + } + fprintf(stderr, "\n"); + } + } + } +} + static void show_help(); /// A thing that fish_test_helper can do. @@ -93,6 +115,7 @@ static fth_command_t s_commands[] = { {"print_pid_then_sleep", print_pid_then_sleep, "Print our pid, then sleep for .5 seconds"}, {"print_pgrp", print_pgrp, "Print our pgroup to stdout"}, {"print_fds", print_fds, "Print the list of active FDs to stdout"}, + {"print_blocked_signals", print_blocked_signals, "Print to stdout the name(s) of blocked signals"}, {"help", show_help, "Print list of fish_test_helper commands"}, }; diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index a197dfa48..8312d12d5 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -810,8 +810,8 @@ static void test_fd_monitor() { item_oneshot.always_exit = true; { fd_monitor_t monitor; - for (auto *item : {&item_never, &item_hugetimeout, &item0_timeout, &item42_timeout, - &item42_nottimeout, &item42_thenclose, &item_oneshot}) { + for (auto item : {&item_never, &item_hugetimeout, &item0_timeout, &item42_timeout, + &item42_nottimeout, &item42_thenclose, &item_oneshot}) { monitor.add(std::move(item->item)); } item42_timeout.write42(); diff --git a/src/function.cpp b/src/function.cpp index 56e5fb2c5..e841d55fc 100644 --- a/src/function.cpp +++ b/src/function.cpp @@ -80,7 +80,7 @@ static owning_lock function_set; bool function_set_t::allow_autoload(const wcstring &name) const { // Prohibit autoloading if we have a non-autoload (explicit) function, or if the function is // tombstoned. - const auto *info = get_info(name); + auto info = get_info(name); bool has_explicit_func = info && !info->is_autoload; bool is_tombstoned = autoload_tombstones.count(name) > 0; return !has_explicit_func && !is_tombstoned; @@ -175,7 +175,7 @@ void function_add(wcstring name, wcstring description, function_properties_ref_t std::shared_ptr function_get_properties(const wcstring &name) { if (parser_keywords_is_reserved(name)) return nullptr; auto funcset = function_set.acquire(); - if (const auto *info = funcset->get_info(name)) { + if (auto info = funcset->get_info(name)) { return info->props; } return nullptr; diff --git a/src/highlight.cpp b/src/highlight.cpp index f94898077..59f4692e3 100644 --- a/src/highlight.cpp +++ b/src/highlight.cpp @@ -253,7 +253,10 @@ bool is_potential_path(const wcstring &potential_path_fragment, const wcstring_l for (const wcstring &wd : directories) { if (ctx.check_cancel()) return false; - const wcstring abs_path = path_apply_working_directory(clean_potential_path_fragment, wd); + wcstring abs_path = path_apply_working_directory(clean_potential_path_fragment, wd); + if (flags & PATH_FOR_CD) { + abs_path = normalize_path(abs_path); + } // Skip this if it's empty or we've already checked it. if (abs_path.empty() || checked_paths.count(abs_path)) continue; @@ -330,7 +333,7 @@ static bool is_potential_cd_path(const wcstring &path, const wcstring &working_d } // Call is_potential_path with all of these directories. - return is_potential_path(path, directories, ctx, flags | PATH_REQUIRE_DIR); + return is_potential_path(path, directories, ctx, flags | PATH_REQUIRE_DIR | PATH_FOR_CD); } // Given a plain statement node in a parse tree, get the command and return it, expanded diff --git a/src/highlight.h b/src/highlight.h index 5e409f8f8..556098084 100644 --- a/src/highlight.h +++ b/src/highlight.h @@ -122,7 +122,9 @@ 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 + PATH_EXPAND_TILDE = 1 << 1, + // Normalize directories before resolving, as "cd". + PATH_FOR_CD = 1 << 2, }; typedef unsigned int path_flags_t; bool is_potential_path(const wcstring &potential_path_fragment, const wcstring_list_t &directories, diff --git a/src/history.cpp b/src/history.cpp index d783035d0..980fc50a5 100644 --- a/src/history.cpp +++ b/src/history.cpp @@ -466,8 +466,7 @@ void history_impl_t::set_valid_file_paths(const wcstring_list_t &valid_file_path } // Look for an item with the given identifier. It is likely to be at the end of new_items. - for (history_item_list_t::reverse_iterator iter = new_items.rbegin(); iter != new_items.rend(); - ++iter) { + for (auto iter = new_items.rbegin(); iter != new_items.rend(); ++iter) { if (iter->identifier == ident) { // found it iter->required_paths = valid_file_paths; break; @@ -603,7 +602,7 @@ void history_impl_t::load_old_if_needed() { bool history_search_t::go_backwards() { // Backwards means increasing our index. - const size_t max_index = static_cast(-1); + const auto max_index = static_cast(-1); if (current_index_ == max_index) return false; diff --git a/src/history_file.cpp b/src/history_file.cpp index 453c96dac..75b905bf6 100644 --- a/src/history_file.cpp +++ b/src/history_file.cpp @@ -29,7 +29,7 @@ static bool should_mmap(int fd) { // Return true on success, false on failure. static bool read_from_fd(int fd, void *address, size_t len) { size_t remaining = len; - char *ptr = static_cast(address); + auto ptr = static_cast(address); while (remaining > 0) { ssize_t amt = read(fd, ptr, remaining); if (amt < 0) { @@ -161,8 +161,8 @@ history_item_t history_file_contents_t::decode_item(size_t offset) const { return history_item_t{}; } -maybe_t history_file_contents_t::offset_of_next_item(size_t *cursor, time_t cutoff) { - size_t offset = size_t(-1); +maybe_t history_file_contents_t::offset_of_next_item(size_t *cursor, time_t cutoff) const { + auto offset = size_t(-1); switch (this->type()) { case history_type_fish_2_0: offset = offset_of_next_item_fish_2_0(*this, cursor, cutoff); @@ -183,7 +183,7 @@ static size_t read_line(const char *base, size_t cursor, size_t len, std::string // Locate the newline. assert(cursor <= len); const char *start = base + cursor; - const char *a_newline = static_cast(std::memchr(start, '\n', len - cursor)); + auto a_newline = static_cast(std::memchr(start, '\n', len - cursor)); if (a_newline != nullptr) { // we found a newline result.assign(start, a_newline - start); // Return the amount to advance the cursor; skip over the newline. @@ -342,7 +342,7 @@ static const char *next_line(const char *start, const char *end) { static size_t offset_of_next_item_fish_2_0(const history_file_contents_t &contents, size_t *inout_cursor, time_t cutoff_timestamp) { size_t cursor = *inout_cursor; - size_t result = size_t(-1); + auto result = size_t(-1); const size_t length = contents.length(); const char *const begin = contents.begin(); const char *const end = contents.end(); @@ -350,8 +350,7 @@ static size_t offset_of_next_item_fish_2_0(const history_file_contents_t &conten const char *line_start = contents.address_at(cursor); // Advance the cursor to the next line. - const char *a_newline = - static_cast(std::memchr(line_start, '\n', length - cursor)); + auto a_newline = static_cast(std::memchr(line_start, '\n', length - cursor)); if (a_newline == nullptr) break; // Advance the cursor past this line. +1 is for the newline. @@ -509,7 +508,7 @@ static history_item_t decode_item_fish_1_x(const char *begin, size_t length) { while (*time_string && !iswdigit(*time_string)) time_string++; if (*time_string) { - time_t tm = static_cast(fish_wcstol(time_string)); + auto tm = static_cast(fish_wcstol(time_string)); if (!errno && tm >= 0) { timestamp = tm; } diff --git a/src/history_file.h b/src/history_file.h index bc5992011..bdd24cfc8 100644 --- a/src/history_file.h +++ b/src/history_file.h @@ -29,7 +29,7 @@ class history_file_contents_t { /// The cursor should initially be 0. /// If cutoff is nonzero, skip items whose timestamp is newer than cutoff. /// \return the offset of the next item, or none() on end. - maybe_t offset_of_next_item(size_t *cursor, time_t cutoff); + maybe_t offset_of_next_item(size_t *cursor, time_t cutoff) const; /// Get the file type. history_file_type_t type() const { return type_; } diff --git a/src/input.cpp b/src/input.cpp index 53da3e7af..0c53d77ca 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -123,6 +123,8 @@ static const input_function_metadata_t input_function_metadata[] = { {readline_cmd_t::upcase_word, L"upcase-word"}, {readline_cmd_t::downcase_word, L"downcase-word"}, {readline_cmd_t::capitalize_word, L"capitalize-word"}, + {readline_cmd_t::togglecase_char, L"togglecase-char"}, + {readline_cmd_t::togglecase_selection, L"togglecase-selection"}, {readline_cmd_t::execute, L"execute"}, {readline_cmd_t::beginning_of_buffer, L"beginning-of-buffer"}, {readline_cmd_t::end_of_buffer, L"end-of-buffer"}, @@ -431,9 +433,9 @@ bool inputter_t::mapping_is_match(const input_mapping_t &m) { return true; } -void inputter_t::queue_ch(const char_event_t &ch) { event_queue_.push_back(std::move(ch)); } +void inputter_t::queue_ch(const char_event_t &ch) { event_queue_.push_back(ch); } -void inputter_t::push_front(const char_event_t &ch) { event_queue_.push_front(std::move(ch)); } +void inputter_t::push_front(const char_event_t &ch) { event_queue_.push_front(ch); } /// \return the first mapping that matches, walking first over the user's mapping list, then the /// preset list. \return null if nothing matches. diff --git a/src/input_common.cpp b/src/input_common.cpp index c5604bbfd..a7897008f 100644 --- a/src/input_common.cpp +++ b/src/input_common.cpp @@ -39,7 +39,7 @@ static int wait_on_escape_ms = WAIT_ON_ESCAPE_DEFAULT; /// Callback function for handling interrupts on reading. static interrupt_func_t interrupt_handler; -void input_common_init(interrupt_func_t func) { interrupt_handler = std::move(func); } +void input_common_init(interrupt_func_t func) { interrupt_handler = func; } /// Internal function used by input_common_readch to read one byte from fd 0. This function should /// only be called by input_common_readch(). diff --git a/src/input_common.h b/src/input_common.h index 746b4f6d6..901acf678 100644 --- a/src/input_common.h +++ b/src/input_common.h @@ -48,6 +48,8 @@ enum class readline_cmd_t { upcase_word, downcase_word, capitalize_word, + togglecase_char, + togglecase_selection, execute, beginning_of_buffer, end_of_buffer, diff --git a/src/io.cpp b/src/io.cpp index 42fabfd46..070dd2687 100644 --- a/src/io.cpp +++ b/src/io.cpp @@ -31,7 +31,7 @@ /// Provide the fd monitor used for background fillthread operations. static fd_monitor_t &fd_monitor() { // Deliberately leaked to avoid shutdown dtors. - static fd_monitor_t *fdm = new fd_monitor_t(); + static auto fdm = new fd_monitor_t(); return *fdm; } @@ -202,7 +202,7 @@ io_buffer_t::~io_buffer_t() { void io_chain_t::remove(const shared_ptr &element) { // See if you can guess why std::find doesn't work here. - for (io_chain_t::iterator iter = this->begin(); iter != this->end(); ++iter) { + for (auto iter = this->begin(); iter != this->end(); ++iter) { if (*iter == element) { this->erase(iter); break; diff --git a/src/iothread.cpp b/src/iothread.cpp index b4a8ea651..c7a32f376 100644 --- a/src/iothread.cpp +++ b/src/iothread.cpp @@ -466,7 +466,7 @@ using void_func_t = std::function; static void *func_invoker(void *param) { // Acquire a thread id for this thread. (void)thread_id(); - void_func_t *vf = static_cast(param); + auto vf = static_cast(param); (*vf)(); delete vf; return nullptr; @@ -474,7 +474,7 @@ static void *func_invoker(void *param) { bool make_detached_pthread(void_func_t &&func) { // Copy the function into a heap allocation. - void_func_t *vf = new void_func_t(std::move(func)); + auto vf = new void_func_t(std::move(func)); if (make_detached_pthread(func_invoker, vf)) { return true; } diff --git a/src/null_terminated_array.cpp b/src/null_terminated_array.cpp index 368584d7f..033f60881 100644 --- a/src/null_terminated_array.cpp +++ b/src/null_terminated_array.cpp @@ -22,13 +22,13 @@ static CharT **make_null_terminated_array_helper( } // Now allocate their sum. - unsigned char *base = + auto base = static_cast(malloc(pointers_allocation_len + strings_allocation_len)); if (!base) return nullptr; // Divvy it up into the pointers and strings. - CharT **pointers = reinterpret_cast(base); - CharT *strings = reinterpret_cast(base + pointers_allocation_len); + auto pointers = reinterpret_cast(base); + auto strings = reinterpret_cast(base + pointers_allocation_len); // Start copying. for (size_t i = 0; i < count; i++) { diff --git a/src/null_terminated_array.h b/src/null_terminated_array.h index 59950be75..ec8bccdc9 100644 --- a/src/null_terminated_array.h +++ b/src/null_terminated_array.h @@ -64,7 +64,7 @@ class null_terminated_array_t { /// Convert from a null terminated list to a vector of strings. static string_list_t to_list(const CharT *const *arr) { string_list_t result; - for (const auto *cursor = arr; cursor && *cursor; cursor++) { + for (auto cursor = arr; cursor && *cursor; cursor++) { result.push_back(*cursor); } return result; diff --git a/src/pager.cpp b/src/pager.cpp index c8a0622b6..78f3bb091 100644 --- a/src/pager.cpp +++ b/src/pager.cpp @@ -84,7 +84,7 @@ static size_t print_max(const wcstring &str, highlight_spec_t color, size_t max, // skip non-printable characters continue; } - size_t width_c = size_t(iwidth_c); + auto width_c = size_t(iwidth_c); if (width_c > remaining) break; @@ -139,7 +139,7 @@ line_t pager_t::completion_print_item(const wcstring &prefix, const comp_t *c, s auto modify_role = [=](highlight_role_t role) -> highlight_role_t { using uint_t = typename std::underlying_type::type; - uint_t base = static_cast(role); + auto base = static_cast(role); if (selected) { base += static_cast(highlight_role_t::pager_selected_background) - static_cast(highlight_role_t::pager_background); @@ -732,7 +732,6 @@ bool pager_t::select_next_completion_in_direction(selection_motion_t direction, } default: { DIE("unknown cardinal direction"); - break; } } diff --git a/src/parse_execution.cpp b/src/parse_execution.cpp index c30382bf9..2935fcdd1 100644 --- a/src/parse_execution.cpp +++ b/src/parse_execution.cpp @@ -189,7 +189,10 @@ maybe_t parse_execution_context_t::check_end_execution() if (shell_is_exiting()) { return end_execution_reason_t::cancelled; } - if (parser && parser->cancellation_signal) { + if (nullptr == parser) { + return none(); + } + if (parser->cancellation_signal) { return end_execution_reason_t::cancelled; } const auto &ld = parser->libdata(); @@ -482,7 +485,7 @@ end_execution_reason_t parse_execution_context_t::run_switch_statement( // If we expanded to nothing, match the empty string. assert(switch_values_expanded.size() <= 1 && "Should have at most one expansion"); - wcstring switch_value_expanded = L""; + wcstring switch_value_expanded; if (!switch_values_expanded.empty()) { switch_value_expanded = std::move(switch_values_expanded.front().completion); } @@ -866,7 +869,6 @@ end_execution_reason_t parse_execution_context_t::expand_arguments_from_nodes( } default: { DIE("unexpected expand_string() return value"); - break; } } @@ -1006,7 +1008,6 @@ end_execution_reason_t parse_execution_context_t::apply_variable_assignments( default: { DIE("unexpected expand_string() return value"); - break; } } wcstring_list_t vals; diff --git a/src/parse_productions.cpp b/src/parse_productions.cpp index a88f54208..6622ebd82 100644 --- a/src/parse_productions.cpp +++ b/src/parse_productions.cpp @@ -178,7 +178,6 @@ RESOLVE(statement) { return production_for(); } } - break; } case parse_token_type_pipe: case parse_token_type_redirection: diff --git a/src/parse_tree.cpp b/src/parse_tree.cpp index 9b9c745b6..46ee115ab 100644 --- a/src/parse_tree.cpp +++ b/src/parse_tree.cpp @@ -453,7 +453,7 @@ class parse_ll_t { // it's lowest on the stack) const size_t child_start_big = nodes.size(); assert(child_start_big < NODE_OFFSET_INVALID); - node_offset_t child_start = static_cast(child_start_big); + auto child_start = static_cast(child_start_big); // To avoid constructing multiple nodes, we make a single one that we modify. parse_node_t representative_child(token_type_invalid); @@ -728,7 +728,7 @@ void parse_ll_t::parse_error_unexpected_token(const wchar_t *expected, parse_tok void parse_ll_t::reset_symbols(enum parse_token_type_t goal) { // Add a new goal node, and then reset our symbol list to point at it. - node_offset_t where = static_cast(nodes.size()); + auto where = static_cast(nodes.size()); nodes.push_back(parse_node_t(goal)); symbol_stack.clear(); diff --git a/src/parse_util.cpp b/src/parse_util.cpp index 9b90737e3..e4ccf6fc0 100644 --- a/src/parse_util.cpp +++ b/src/parse_util.cpp @@ -1014,7 +1014,6 @@ parser_test_error_bits_t parse_util_detect_errors_in_argument(tnode_t #include -#if FISH_USE_POSIX_SPAWN +#ifdef FISH_USE_POSIX_SPAWN #include #endif #include @@ -135,7 +135,7 @@ bool set_child_group(job_t *j, pid_t child_pid) { return true; } -int child_setup_process(pid_t new_termowner, bool is_forked, const dup2_list_t &dup2s) { +int child_setup_process(pid_t new_termowner, const job_t &job, bool is_forked, const dup2_list_t &dup2s) { // Note we are called in a forked child. for (const auto &act : dup2s.get_actions()) { int err; @@ -170,6 +170,11 @@ int child_setup_process(pid_t new_termowner, bool is_forked, const dup2_list_t & (void)tcsetpgrp(STDIN_FILENO, new_termowner); } } + sigset_t sigmask; + sigemptyset(&sigmask); + if (blocked_signals_for_job(job, &sigmask)) { + sigprocmask(SIG_SETMASK, &sigmask, nullptr); + } // Set the handling for job control signals back to the default. // Do this after any tcsetpgrp call so that we swallow SIGTTIN. signal_reset_handlers(); @@ -275,7 +280,10 @@ bool fork_actions_make_spawn_properties(posix_spawnattr_t *attr, // No signals blocked. sigset_t sigmask; sigemptyset(&sigmask); - if (!err && reset_sigmask) err = posix_spawnattr_setsigmask(attr, &sigmask); + if (!err && reset_sigmask) { + blocked_signals_for_job(*j, &sigmask); + err = posix_spawnattr_setsigmask(attr, &sigmask); + } // Apply our dup2s. for (const auto &act : dup2s.get_actions()) { diff --git a/src/postfork.h b/src/postfork.h index 442e666f3..2e7abc47b 100644 --- a/src/postfork.h +++ b/src/postfork.h @@ -31,7 +31,7 @@ bool child_set_group(job_t *j, process_t *p); // called by child /// /// \return 0 on success, -1 on failure. When this function returns, signals are always unblocked. /// On failure, signal handlers, io redirections and process group of the process is undefined. -int child_setup_process(pid_t new_termowner, bool is_forked, const dup2_list_t &dup2s); +int child_setup_process(pid_t new_termowner, const job_t &job, bool is_forked, const dup2_list_t &dup2s); /// Call fork(), retrying on failure a few times. pid_t execute_fork(); @@ -40,7 +40,7 @@ pid_t execute_fork(); void safe_report_exec_error(int err, const char *actual_cmd, const char *const *argv, const char *const *envv); -#if FISH_USE_POSIX_SPAWN +#ifdef 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, diff --git a/src/proc.cpp b/src/proc.cpp index 18785ba03..d55aab54e 100644 --- a/src/proc.cpp +++ b/src/proc.cpp @@ -96,7 +96,7 @@ void proc_init() { signal_set_handlers_once(false); } // Basic thread safe sorted vector of job IDs in use. // This is deliberately leaked to avoid dtor ordering issues - see #6539. -static auto *const locked_consumed_job_ids = new owning_lock>(); +static const auto locked_consumed_job_ids = new owning_lock>(); job_id_t acquire_job_id() { auto consumed_job_ids = locked_consumed_job_ids->acquire(); @@ -187,7 +187,11 @@ statuses_t job_t::get_statuses() const { statuses_t st{}; st.pipestatus.reserve(processes.size()); for (const auto &p : processes) { - st.pipestatus.push_back(p->status.status_value()); + auto status = p->status; + if (status.signal_exited()) { + st.kill_signal = status.signal_code(); + } + st.pipestatus.push_back(status.status_value()); } int laststatus = st.pipestatus.back(); st.status = flags().negate ? !laststatus : laststatus; @@ -424,7 +428,7 @@ static void process_mark_finished_children(parser_t &parser, bool block_ok) { assert(pid == proc->pid && "Unexpcted waitpid() return"); handle_child_status(proc.get(), proc_status_t::from_waitpid(status)); if (proc->status.stopped()) { - j->mut_flags().foreground = 0; + j->mut_flags().foreground = false; } if (proc->status.normal_exited() || proc->status.signal_exited()) { FLOGF(proc_reap_external, @@ -759,7 +763,7 @@ int terminal_maybe_give_to_job(const job_t *j, bool continuing_from_stopped) { return notneeded; } - if (j->pgid == 0) { + if (j->pgid == INVALID_PID || j->pgid == 0) { FLOG(proc_termowner, L"terminal_give_to_job() returning early due to no process group"); return notneeded; } @@ -779,7 +783,22 @@ int terminal_maybe_give_to_job(const job_t *j, bool continuing_from_stopped) { // http://curiousthing.org/sigttin-sigttou-deep-dive-linux In all cases, our goal here was just // to hand over control of the terminal to this process group, which is a no-op if it's already // been done. - if (j->pgid == INVALID_PID || tcgetpgrp(STDIN_FILENO) == j->pgid) { + int getpgrp_res = tcgetpgrp(STDIN_FILENO); + if (getpgrp_res < 0) { + if (errno == ENOTTY) { + // stdin is not a tty. This may come about if job control is enabled but we are not a + // tty - see #6573. + return notneeded; + } else if (errno == EBADF) { + // stdin has been closed. Workaround a glibc bug - see #3644. + redirect_tty_output(); + return notneeded; + } + wperror(L"tcgetpgrp"); + return error; + } + assert(getpgrp_res >= 0); + if (getpgrp_res == j->pgid) { FLOGF(proc_termowner, L"Process group %d already has control of terminal", j->pgid); } else { FLOGF(proc_termowner, @@ -799,7 +818,6 @@ int terminal_maybe_give_to_job(const job_t *j, bool continuing_from_stopped) { FLOGF(proc_termowner, L"tcsetpgrp failed: %d", errno); bool pgroup_terminated = false; - // No need to test for EINTR as we are blocking signals if (errno == EINVAL) { // OS X returns EINVAL if the process group no longer lives. Probably other OSes, // too. Unlike EPERM below, EINVAL can only happen if the process group has @@ -823,7 +841,9 @@ int terminal_maybe_give_to_job(const job_t *j, bool continuing_from_stopped) { } } else { if (errno == ENOTTY) { - redirect_tty_output(); + // stdin is not a TTY. In general we expect this to be caught via the tcgetpgrp + // call. + return notneeded; } else { FLOGF(warning, _(L"Could not send job %d ('%ls') with pgid %d to foreground"), j->job_id(), j->command_wcstr(), j->pgid); @@ -850,15 +870,7 @@ int terminal_maybe_give_to_job(const job_t *j, bool continuing_from_stopped) { if (continuing_from_stopped) { auto result = tcsetattr(STDIN_FILENO, TCSADRAIN, &j->tmodes); if (result == -1) { - // No need to test for EINTR and retry since we have blocked all signals - if (errno == ENOTTY) { - redirect_tty_output(); - } - - FLOGF(warning, _(L"Could not send job %d ('%ls') to foreground"), j->job_id(), - j->preview().c_str()); wperror(L"tcsetattr"); - return error; } } diff --git a/src/reader.cpp b/src/reader.cpp index a35bd2f51..ed894f967 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -148,13 +148,13 @@ operation_context_t get_bg_context(const std::shared_ptr &env, /// These are deliberately leaked to avoid shutdown dtor registration. static debounce_t &debounce_autosuggestions() { const long kAutosuggetTimeoutMs = 500; - static debounce_t *res = new debounce_t(kAutosuggetTimeoutMs); + static auto res = new debounce_t(kAutosuggetTimeoutMs); return *res; } static debounce_t &debounce_highlighting() { const long kHighlightTimeoutMs = 500; - static debounce_t *res = new debounce_t(kHighlightTimeoutMs); + static auto res = new debounce_t(kHighlightTimeoutMs); return *res; } @@ -240,7 +240,7 @@ bool editable_line_t::undo() { edit_t inverse = edit_t(edit.offset, edit.replacement.size(), L""); inverse.replacement = edit.old; size_t old_position = edit.cursor_position_before_edit; - apply_edit(&text_, std::move(inverse)); + apply_edit(&text_, inverse); set_position(old_position); undo_history.may_coalesce = false; return true; @@ -549,7 +549,7 @@ class reader_data_t : public std::enable_shared_from_this { } editable_line_t *active_edit_line() { - const auto *cthis = this; + auto cthis = reinterpret_cast(this); return const_cast(cthis->active_edit_line()); } @@ -864,7 +864,7 @@ void reader_data_t::pager_selection_changed() { } // Trigger repaint (see issue #765). - reader_repaint_needed(); + mark_repaint_needed(); } /// Expand abbreviations at the given cursor position. Does NOT inspect 'data'. @@ -1774,7 +1774,7 @@ bool reader_data_t::handle_completions(const completion_list_t &comp, size_t tok current_page_rendering = page_rendering_t(); // Modify the command line to reflect the new pager. pager_selection_changed(); - reader_repaint_needed(); + mark_repaint_needed(); return false; } @@ -2105,7 +2105,7 @@ void reader_data_t::set_buffer_maintaining_pager(const wcstring &b, size_t pos, // Clear history search and pager contents. history_search.reset(); super_highlight_me_plenty(); - reader_repaint_needed(); + mark_repaint_needed(); } void set_env_cmd_duration(struct timeval *after, struct timeval *before, env_stack_t &vars) { @@ -2330,10 +2330,10 @@ void reader_set_expand_abbreviations(bool flag) { current_data()->expand_abbrevi void reader_set_complete_ok(bool flag) { current_data()->complete_ok = flag; } void reader_set_highlight_function(highlight_function_t func) { - current_data()->highlight_func = std::move(func); + current_data()->highlight_func = func; } -void reader_set_test_function(test_function_t f) { current_data()->test_func = std::move(f); } +void reader_set_test_function(test_function_t f) { current_data()->test_func = f; } void reader_set_exit_on_interrupt(bool i) { current_data()->exit_on_interrupt = i; } @@ -2536,7 +2536,7 @@ static bool event_is_normal_char(const char_event_t &evt) { } /// readline_loop_state_t encapsulates the state used in a readline loop. -/// It is always stack allocated transient. This state should not be "publicly visible;" public +/// It is always stack allocated transient. This state should not be "publicly visible"; public /// state should be in reader_data_t. struct readline_loop_state_t { /// The last command that was executed. @@ -2545,8 +2545,8 @@ struct readline_loop_state_t { /// If the last command was a yank, the length of yanking that occurred. size_t yank_len{0}; - /// If set, it means nothing has been inserted into the command line via completion machinery. - bool comp_empty{true}; + /// If the last "complete" readline command has inserted text into the command line. + bool complete_did_insert{true}; /// List of completions. completion_list_t comp; @@ -2609,7 +2609,7 @@ void reader_data_t::handle_readline_command(readline_cmd_t c, readline_loop_stat while (el->position() > 0 && el->text().at(el->position() - 1) != L'\n') { update_buff_pos(el, el->position() - 1); } - reader_repaint_needed(); + mark_repaint_needed(); break; } case rl::end_of_line: { @@ -2623,17 +2623,17 @@ void reader_data_t::handle_readline_command(readline_cmd_t c, readline_loop_stat accept_autosuggestion(true); } - reader_repaint_needed(); + mark_repaint_needed(); break; } case rl::beginning_of_buffer: { update_buff_pos(&command_line, 0); - reader_repaint_needed(); + mark_repaint_needed(); break; } case rl::end_of_buffer: { update_buff_pos(&command_line, command_line.size()); - reader_repaint_needed(); + mark_repaint_needed(); break; } case rl::cancel: { @@ -2676,12 +2676,12 @@ void reader_data_t::handle_readline_command(readline_cmd_t c, readline_loop_stat // Use the command line only; it doesn't make sense to complete in any other line. editable_line_t *el = &command_line; if (is_navigating_pager_contents() || - (!rls.comp_empty && rls.last_cmd == rl::complete)) { + (!rls.complete_did_insert && rls.last_cmd == rl::complete)) { // The user typed complete more than once in a row. If we are not yet fully // disclosed, then become so; otherwise cycle through our available completions. if (current_page_rendering.remaining_to_disclose > 0) { pager.set_fully_disclosed(true); - reader_repaint_needed(); + mark_repaint_needed(); } else { select_completion_in_direction(c == rl::complete ? selection_motion_t::next : selection_motion_t::prev); @@ -2735,14 +2735,14 @@ void reader_data_t::handle_readline_command(readline_cmd_t c, readline_loop_stat cycle_cursor_pos = token_end - buff; bool cont_after_prefix_insertion = (c == rl::complete_and_search); - rls.comp_empty = handle_completions(rls.comp, token_begin - buff, token_end - buff, + rls.complete_did_insert = handle_completions(rls.comp, token_begin - buff, token_end - buff, cont_after_prefix_insertion); // Show the search field if requested and if we printed a list of completions. - if (c == rl::complete_and_search && !rls.comp_empty && !pager.empty()) { + if (c == rl::complete_and_search && !rls.complete_did_insert && !pager.empty()) { pager.set_search_field_shown(true); select_completion_in_direction(selection_motion_t::next); - reader_repaint_needed(); + mark_repaint_needed(); } } break; @@ -2756,7 +2756,7 @@ void reader_data_t::handle_readline_command(readline_cmd_t c, readline_loop_stat if (pager.is_search_field_shown() && !is_navigating_pager_contents()) { select_completion_in_direction(selection_motion_t::south); } - reader_repaint_needed(); + mark_repaint_needed(); } break; } @@ -2946,7 +2946,7 @@ void reader_data_t::handle_readline_command(readline_cmd_t c, readline_loop_stat // Result must be some combination including an error. The error message will // already be printed, all we need to do is repaint. s_reset(&screen, screen_reset_abandon_line); - reader_repaint_needed(); + mark_repaint_needed(); } break; @@ -3010,7 +3010,7 @@ void reader_data_t::handle_readline_command(readline_cmd_t c, readline_loop_stat select_completion_in_direction(selection_motion_t::west); } else if (el->position() > 0) { update_buff_pos(el, el->position() - 1); - reader_repaint_needed(); + mark_repaint_needed(); } break; } @@ -3020,7 +3020,7 @@ void reader_data_t::handle_readline_command(readline_cmd_t c, readline_loop_stat select_completion_in_direction(selection_motion_t::east); } else if (el->position() < el->size()) { update_buff_pos(el, el->position() + 1); - reader_repaint_needed(); + mark_repaint_needed(); } else { accept_autosuggestion(true); } @@ -3144,7 +3144,7 @@ void reader_data_t::handle_readline_command(readline_cmd_t c, readline_loop_stat total_offset_new = parse_util_get_offset( el->text(), line_new, line_offset_old - 4 * (indent_new - indent_old)); update_buff_pos(el, total_offset_new); - reader_repaint_needed(); + mark_repaint_needed(); } } break; @@ -3152,7 +3152,7 @@ void reader_data_t::handle_readline_command(readline_cmd_t c, readline_loop_stat case rl::suppress_autosuggestion: { suppress_autosuggestion = true; autosuggestion.clear(); - reader_repaint_needed(); + mark_repaint_needed(); break; } case rl::accept_autosuggestion: { @@ -3219,6 +3219,72 @@ void reader_data_t::handle_readline_command(readline_cmd_t c, readline_loop_stat } break; } + case rl::togglecase_char: { + editable_line_t *el = active_edit_line(); + size_t buff_pos = el->position(); + + // Check that the cursor is on a character + if (buff_pos < el->size()) { + wchar_t chr = el->text().at(buff_pos); + wcstring replacement; + + // Toggle the case of the current character + bool make_uppercase = iswlower(chr); + if (make_uppercase) { + chr = towupper(chr); + } else { + chr = tolower(chr); + } + + replacement.push_back(chr); + el->replace_substring(buff_pos, (size_t)1, std::move(replacement)); + + // Restore the buffer position since replace_substring moves + // the buffer position ahead of the replaced text. + update_buff_pos(el, buff_pos); + + command_line_changed(el); + super_highlight_me_plenty(); + mark_repaint_needed(); + } + break; + } + case rl::togglecase_selection: { + editable_line_t *el = active_edit_line(); + + // Check that we have an active selection and get the bounds. + size_t start, len; + if (reader_get_selection(&start, &len)) { + size_t buff_pos = el->position(); + wcstring replacement; + + // Loop through the selected characters and toggle their case. + for (size_t pos = start; pos < start + len && pos < el->size(); pos++) { + wchar_t chr = el->text().at(pos); + + // Toggle the case of the current character. + bool make_uppercase = iswlower(chr); + if (make_uppercase) { + chr = towupper(chr); + } else { + chr = tolower(chr); + } + + replacement.push_back(chr); + } + + el->replace_substring(start, len, std::move(replacement)); + + // Restore the buffer position since replace_substring moves + // the buffer position ahead of the replaced text. + update_buff_pos(el, buff_pos); + + command_line_changed(el); + super_highlight_me_plenty(); + mark_repaint_needed(); + } + break; + } case rl::upcase_word: case rl::downcase_word: case rl::capitalize_word: { @@ -3255,7 +3321,7 @@ void reader_data_t::handle_readline_command(readline_cmd_t c, readline_loop_stat update_buff_pos(el); command_line_changed(el); super_highlight_me_plenty(); - reader_repaint_needed(); + mark_repaint_needed(); break; } case rl::begin_selection: @@ -3304,7 +3370,7 @@ void reader_data_t::handle_readline_command(readline_cmd_t c, readline_loop_stat bool success = jump(direction, precision, el, target); inputter.function_set_status(success); - reader_repaint_needed(); + mark_repaint_needed(); break; } case rl::repeat_jump: { @@ -3316,7 +3382,7 @@ void reader_data_t::handle_readline_command(readline_cmd_t c, readline_loop_stat } inputter.function_set_status(success); - reader_repaint_needed(); + mark_repaint_needed(); break; } case rl::reverse_repeat_jump: { @@ -3338,7 +3404,7 @@ void reader_data_t::handle_readline_command(readline_cmd_t c, readline_loop_stat last_jump_direction = original_dir; inputter.function_set_status(success); - reader_repaint_needed(); + mark_repaint_needed(); break; } diff --git a/src/sanity.cpp b/src/sanity.cpp index cbe4b0e82..57cf5a6dc 100644 --- a/src/sanity.cpp +++ b/src/sanity.cpp @@ -24,7 +24,7 @@ void sanity_lose() { void validate_pointer(const void *ptr, const wchar_t *err, int null_ok) { // Test if the pointer data crosses a segment boundary. - if ((0x00000003l & (intptr_t)ptr) != 0) { + if ((0x00000003L & (intptr_t)ptr) != 0) { FLOGF(error, _(L"The pointer '%ls' is invalid"), err); sanity_lose(); } diff --git a/src/signal.cpp b/src/signal.cpp index c17587cea..e4d1f1d7a 100644 --- a/src/signal.cpp +++ b/src/signal.cpp @@ -407,7 +407,7 @@ bool sigint_checker_t::check() { return changed; } -void sigint_checker_t::wait() { +void sigint_checker_t::wait() const { auto &tm = topic_monitor_t::principal(); generation_list_t gens{}; gens[topic_t::sighupint] = this->gen_; diff --git a/src/signal.h b/src/signal.h index af284f36c..88bec4c2a 100644 --- a/src/signal.h +++ b/src/signal.h @@ -45,7 +45,7 @@ class sigint_checker_t { bool check(); /// Wait until a sigint is delivered. - void wait(); + void wait() const; }; #endif diff --git a/src/timer.cpp b/src/timer.cpp index 8ebae52dc..248b9144b 100644 --- a/src/timer.cpp +++ b/src/timer.cpp @@ -201,13 +201,13 @@ wcstring timer_snapshot_t::print_delta(timer_snapshot_t t1, timer_snapshot_t t2, static std::vector active_timers; static void pop_timer() { - auto t1 = std::move(active_timers.back()); + auto t1 = active_timers.back(); active_timers.pop_back(); auto t2 = timer_snapshot_t::take(); // Well, this is awkward. By defining `time` as a decorator and not a built-in, there's // no associated stream for its output! - auto output = timer_snapshot_t::print_delta(std::move(t1), std::move(t2), true); + auto output = timer_snapshot_t::print_delta(t1, t2, true); std::fwprintf(stderr, L"%S\n", output.c_str()); } diff --git a/src/tinyexpr.cpp b/src/tinyexpr.cpp index e134b6cb7..e67cc2667 100644 --- a/src/tinyexpr.cpp +++ b/src/tinyexpr.cpp @@ -74,13 +74,13 @@ typedef struct te_expr { te_expr *parameters[]; } te_expr; -typedef struct te_builtin { +using te_builtin = struct { const char *name; const void *address; int type; -} te_builtin; +}; -typedef struct state { +using state = struct { union { double value; const void *function; @@ -89,7 +89,7 @@ typedef struct state { const char *next; int type; te_error_type_t error; -} state; +}; /* Parses the input expression. */ /* Returns NULL on error. */ @@ -109,7 +109,7 @@ static te_expr *new_expr(const int type, const te_expr *parameters[]) { const int arity = get_arity(type); const int psize = sizeof(te_expr *) * arity; const int size = sizeof(te_expr) + psize; - te_expr *ret = static_cast(malloc(size)); + auto ret = static_cast(malloc(size)); // This sets float to 0, which depends on the implementation. // We rely on IEEE-754 floats anyway, so it's okay. std::memset(ret, 0, size); @@ -142,7 +142,7 @@ static constexpr double e() { return M_E; } static double fac(double a) { /* simplest version of fac */ if (a < 0.0) return NAN; if (a > UINT_MAX) return INFINITY; - unsigned int ua = static_cast(a); + auto ua = static_cast(a); unsigned long int result = 1, i; for (i = 1; i <= ua; i++) { if (i > ULONG_MAX / result) return INFINITY; @@ -448,7 +448,7 @@ static te_expr *factor(state *s) { while (s->type == TOK_INFIX && (s->function == reinterpret_cast(static_cast(pow)))) { - te_fun2 t = (te_fun2)s->function; + auto t = (te_fun2)s->function; next_token(s); if (insertion) { @@ -475,7 +475,7 @@ static te_expr *term(state *s) { (s->function == reinterpret_cast(static_cast(mul)) || s->function == reinterpret_cast(static_cast(divide)) || s->function == reinterpret_cast(static_cast(fmod)))) { - te_fun2 t = (te_fun2)s->function; + auto t = (te_fun2)s->function; next_token(s); ret = NEW_EXPR(TE_FUNCTION2, ret, factor(s)); ret->function = reinterpret_cast(t); @@ -489,7 +489,7 @@ static te_expr *expr(state *s) { te_expr *ret = term(s); while (s->type == TOK_INFIX && (s->function == add || s->function == sub)) { - te_fun2 t = (te_fun2)s->function; + auto t = (te_fun2)s->function; next_token(s); ret = NEW_EXPR(TE_FUNCTION2, ret, term(s)); ret->function = reinterpret_cast(t); diff --git a/src/tnode.h b/src/tnode.h index cfe3fd17b..ef2f59a1f 100644 --- a/src/tnode.h +++ b/src/tnode.h @@ -97,7 +97,7 @@ class tnode_t { uint8_t child_count() const { return nodeptr ? nodeptr->child_count : 0; } maybe_t source_range() const { - if (nodeptr->source_start == NODE_OFFSET_INVALID) return none(); + if (!nodeptr || nodeptr->source_start == NODE_OFFSET_INVALID) return none(); return source_range_t{nodeptr->source_start, nodeptr->source_length}; } diff --git a/src/topic_monitor.cpp b/src/topic_monitor.cpp index 779c5bffb..7a5fe7816 100644 --- a/src/topic_monitor.cpp +++ b/src/topic_monitor.cpp @@ -16,11 +16,11 @@ // block) and use use select() to poll it. #if defined(__has_feature) #if __has_feature(thread_sanitizer) -#define TOPIC_MONITOR_TSAN_WORKAROUND 1 +#define TOPIC_MONITOR_TSAN_WORKAROUND #endif #endif -#if __SANITIZE_THREAD__ -#define TOPIC_MONITOR_TSAN_WORKAROUND 1 +#ifdef __SANITIZE_THREAD__ +#define TOPIC_MONITOR_TSAN_WORKAROUND #endif /// Implementation of the principal monitor. This uses new (and leaks) to avoid registering a @@ -49,7 +49,7 @@ topic_monitor_t::topic_monitor_t() { // The read end must block to avoid spinning in await. DIE_ON_FAILURE(make_fd_nonblocking(pipes_.write.fd())); -#if TOPIC_MONITOR_TSAN_WORKAROUND +#ifdef TOPIC_MONITOR_TSAN_WORKAROUND DIE_ON_FAILURE(make_fd_nonblocking(pipes_.read.fd())); #endif } @@ -144,7 +144,7 @@ generation_list_t topic_monitor_t::await_gens(const generation_list_t &input_gen assert(gens == input_gens && "Generations should not have changed if we are the reader."); int fd = pipes_.read.fd(); -#if TOPIC_MONITOR_TSAN_WORKAROUND +#ifdef TOPIC_MONITOR_TSAN_WORKAROUND // Under tsan our notifying pipe is non-blocking, so we would busy-loop on the read() // call until data is available (that is, fish would use 100% cpu while waiting for // processes). The select prevents that. diff --git a/src/utf8.cpp b/src/utf8.cpp index 1086fd8f1..1efb9b950 100644 --- a/src/utf8.cpp +++ b/src/utf8.cpp @@ -67,7 +67,7 @@ bool wchar_to_utf8_string(const std::wstring &str, std::string *result) { const wchar_t *input = str.c_str(); size_t outlen = wchar_to_utf8(input, inlen, nullptr, 0, 0); if (outlen > 0) { - char *tmp = new char[outlen]; + auto tmp = new char[outlen]; size_t outlen2 = wchar_to_utf8(input, inlen, tmp, outlen, 0); if (outlen2 > 0) { result->assign(tmp, outlen2); @@ -110,7 +110,7 @@ size_t wchar_to_utf8(const wchar_t *in, size_t insize, char *out, size_t outsize } else { // Allocate a temporary buffer to hold the input the std::copy performs the size conversion. // Note: insize may be 0. - utf8_wchar_t *tmp_input = new utf8_wchar_t[insize]; + auto tmp_input = new utf8_wchar_t[insize]; if (!safe_copy_wchar_to_utf8_wchar(in, tmp_input, insize)) { // Our utf8_wchar_t is UCS-16 and there was an astral character. result = 0; @@ -345,7 +345,6 @@ static size_t wchar_to_utf8_internal(const utf8_wchar_t *in, size_t insize, char } default: { DIE("unexpected utff8 len"); - break; } } diff --git a/src/util.cpp b/src/util.cpp index c23404783..b2fb04299 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -153,5 +153,5 @@ int wcsfilecmp_glob(const wchar_t *a, const wchar_t *b) { long long get_time() { struct timeval time_struct; gettimeofday(&time_struct, nullptr); - return 1000000ll * time_struct.tv_sec + time_struct.tv_usec; + return 1000000LL * time_struct.tv_sec + time_struct.tv_usec; } diff --git a/src/wgetopt.cpp b/src/wgetopt.cpp index f64515763..127f8e919 100644 --- a/src/wgetopt.cpp +++ b/src/wgetopt.cpp @@ -302,7 +302,7 @@ void wgetopter_t::_update_long_opt(int argc, wchar_t **argv, const struct woptio // Find a matching long opt. const struct woption *wgetopter_t::_find_matching_long_opt(const struct woption *longopts, wchar_t *nameend, int *exact, int *ambig, - int *indfound) { + int *indfound) const { const struct woption *pfound = nullptr; int option_index = 0; diff --git a/src/wgetopt.h b/src/wgetopt.h index ae5ed0b21..496c27285 100644 --- a/src/wgetopt.h +++ b/src/wgetopt.h @@ -54,7 +54,7 @@ class wgetopter_t { bool _handle_long_opt(int argc, wchar_t **argv, const struct woption *longopts, int *longind, int long_only, int *retval); const struct woption *_find_matching_long_opt(const struct woption *longopts, wchar_t *nameend, - int *exact, int *ambig, int *indfound); + int *exact, int *ambig, int *indfound) const; void _update_long_opt(int argc, wchar_t **argv, const struct woption *pfound, wchar_t *nameend, int *longind, int option_index, int *retval); bool initialized = false; diff --git a/src/wildcard.cpp b/src/wildcard.cpp index af397acb3..2b2a1e88c 100644 --- a/src/wildcard.cpp +++ b/src/wildcard.cpp @@ -166,7 +166,7 @@ static wcstring resolve_description(const wcstring &full_completion, wcstring *c size_t complete_sep_loc = completion->find(PROG_COMPLETE_SEP); if (complete_sep_loc != wcstring::npos) { // This completion has an embedded description, do not use the generic description. - const wcstring description = completion->substr(complete_sep_loc + 1); + wcstring description = completion->substr(complete_sep_loc + 1); completion->resize(complete_sep_loc); return description; } @@ -218,7 +218,7 @@ static bool wildcard_complete_internal(const wchar_t *str, const wchar_t *wc, // Maybe we have no more wildcards at all. This includes the empty string. if (next_wc_char_pos == wcstring::npos) { - string_fuzzy_match_t match = string_fuzzy_match_string(wc, str); + auto match = string_fuzzy_match_string(wc, str); // If we're allowing fuzzy match, any match is OK. Otherwise we require a prefix match. bool match_acceptable; @@ -306,7 +306,6 @@ static bool wildcard_complete_internal(const wchar_t *str, const wchar_t *wc, } default: { DIE("unreachable code reached"); - break; } } diff --git a/src/wutil.cpp b/src/wutil.cpp index 495b26fff..3eb90c507 100644 --- a/src/wutil.cpp +++ b/src/wutil.cpp @@ -206,7 +206,7 @@ dir_t::~dir_t() { bool dir_t::valid() const { return this->dir != nullptr; } -bool dir_t::read(wcstring &name) { return wreaddir(this->dir, name); } +bool dir_t::read(wcstring &name) const { return wreaddir(this->dir, name); } int wstat(const wcstring &file_name, struct stat *buf) { const cstring tmp = wcs2string(file_name); @@ -268,7 +268,7 @@ int fd_check_is_remote(int fd) { switch ((unsigned int)buf.f_type) { case 0x6969: // NFS_SUPER_MAGIC case 0x517B: // SMB_SUPER_MAGIC - case 0xFF534D42u: // CIFS_MAGIC_NUMBER + case 0xFF534D42U: // CIFS_MAGIC_NUMBER return 1; default: // Other FSes are assumed local. diff --git a/src/wutil.h b/src/wutil.h index c142e6853..db98b7221 100644 --- a/src/wutil.h +++ b/src/wutil.h @@ -173,7 +173,7 @@ struct file_id_t { struct dir_t { DIR *dir; bool valid() const; - bool read(wcstring &name); + bool read(wcstring &name) const; dir_t(const wcstring &path); ~dir_t(); }; diff --git a/tests/bind.expect b/tests/bind.expect index 3b9a3290c..5ffc364d6 100644 --- a/tests/bind.expect +++ b/tests/bind.expect @@ -191,6 +191,19 @@ expect_prompt -re {\r\nTENT\r\n} { puts stderr "Couldn't find expected output 'TENT'" } +# Test '~' (togglecase-char) +send "\033" +sleep 0.300 +send "ccecho some TExT\033" +sleep 0.300 +send "hh~~bbve~\r" +expect_prompt -re {\r\nSOME TeXT\r\n} { +} -nounmatched -re {\r\nfail} { + puts stderr "~-binding fail" +} unmatched { + puts stderr "Couldn't find expected output 'SOME TeXT'" +} + # Now test that exactly the expected bind modes are defined send "bind --list-modes\r" expect_prompt -re {\r\ndefault\r\ninsert\r\npaste\r\nreplace\r\nreplace_one\r\nvisual\r\n} { diff --git a/tests/checks/alias.fish b/tests/checks/alias.fish index 569ee125e..e89c39377 100644 --- a/tests/checks/alias.fish +++ b/tests/checks/alias.fish @@ -9,6 +9,7 @@ my_alias # CHECK: foo ran alias a-2='echo "hello there"' +alias a-3='echo hello\ there' alias foo '"a b" c d e' # Bare `alias` should list the aliases we have created and nothing else @@ -16,5 +17,6 @@ alias foo '"a b" c d e' # framework and we can't predict the definition. alias | grep -Ev '^alias (fish_indent|fish_key_reader) ' # CHECK: alias a-2 'echo "hello there"' +# CHECK: alias a-3 echo\\\ hello\\\\\\\ there # CHECK: alias foo '"a b" c d e' # CHECK: alias my_alias 'foo; and echo foo ran' diff --git a/tests/checks/argparse.fish b/tests/checks/argparse.fish index 642b20fac..dfeea3834 100644 --- a/tests/checks/argparse.fish +++ b/tests/checks/argparse.fish @@ -227,7 +227,7 @@ and echo unxpected argparse return status >&2 # CHECKERR: argparse: Value 'a1' for flag 'm' is not an integer # Check the exit status from argparse validation -argparse 'm#max!set | grep _flag_; function x; return 57; end; x' -- argle --max=83 bargle 2>&1 +argparse 'm#max!set | grep "^_flag_"; function x; return 57; end; x' -- argle --max=83 bargle 2>&1 set -l saved_status $status test $saved_status -eq 57 and echo expected argparse return status $saved_status diff --git a/tests/checks/complete.fish b/tests/checks/complete.fish index f07d062a4..1d9b17bbe 100644 --- a/tests/checks/complete.fish +++ b/tests/checks/complete.fish @@ -62,7 +62,6 @@ complete # CHECK: complete {{.*}} # CHECK: complete {{.*}} # CHECK: complete {{.*}} -# CHECK: complete {{.*}} # Recursive calls to complete (see #3474) complete -c complete_test_recurse1 -xa '(echo recursing 1>&2; complete -C"complete_test_recurse1 ")' diff --git a/tests/checks/interactive.fish b/tests/checks/interactive.fish deleted file mode 100644 index 27bef3c20..000000000 --- a/tests/checks/interactive.fish +++ /dev/null @@ -1,3 +0,0 @@ -#RUN: %fish -c 'if status --is-login ; echo login shell ; else ; echo not login shell ; end; if status --is-interactive ; echo interactive ; else ; echo not interactive ; end' -i -# CHECK: not login shell -# CHECK: interactive diff --git a/tests/checks/invocation.fish b/tests/checks/invocation.fish index f10809118..b2b843293 100644 --- a/tests/checks/invocation.fish +++ b/tests/checks/invocation.fish @@ -10,3 +10,31 @@ echo $status PATH= $fish -c "echo (command a)" 2>/dev/null echo $status # CHECK: 127 + +if not set -q GITHUB_WORKFLOW + $fish -c 'if status --is-login ; echo login shell ; else ; echo not login shell ; end; if status --is-interactive ; echo interactive ; else ; echo not interactive ; end' -i + # CHECK: not login shell + # CHECK: interactive + $fish -c 'if status --is-login ; echo login shell ; else ; echo not login shell ; end; if status --is-interactive ; echo interactive ; else ; echo not interactive ; end' -l -i + # CHECK: login shell + # CHECK: interactive +else + # Github Action doesn't start this in a terminal, so fish would complain. + # Instead, we just fake the result, since we have no way to indicate a skipped test. + echo not login shell + echo interactive + echo login shell + echo interactive +end + +$fish -c 'if status --is-login ; echo login shell ; else ; echo not login shell ; end; if status --is-interactive ; echo interactive ; else ; echo not interactive ; end' -l +# CHECK: login shell +# CHECK: not interactive + +$fish -c 'if status --is-login ; echo login shell ; else ; echo not login shell ; end; if status --is-interactive ; echo interactive ; else ; echo not interactive ; end' +# CHECK: not login shell +# CHECK: not interactive + +$fish -c 'if status --is-login ; echo login shell ; else ; echo not login shell ; end; if status --is-interactive ; echo interactive ; else ; echo not interactive ; end' -l +# CHECK: login shell +# CHECK: not interactive diff --git a/tests/checks/job-control-not-a-tty.fish b/tests/checks/job-control-not-a-tty.fish new file mode 100644 index 000000000..80b780549 --- /dev/null +++ b/tests/checks/job-control-not-a-tty.fish @@ -0,0 +1,6 @@ +#RUN: echo 'status job-control full; command echo A ; echo B;' | %fish + +# Regression test for #6573. + +#CHECK: A +#CHECK: B diff --git a/tests/checks/login-interactive.fish b/tests/checks/login-interactive.fish deleted file mode 100644 index 055725a3c..000000000 --- a/tests/checks/login-interactive.fish +++ /dev/null @@ -1,3 +0,0 @@ -#RUN: %fish -c 'if status --is-login ; echo login shell ; else ; echo not login shell ; end; if status --is-interactive ; echo interactive ; else ; echo not interactive ; end' -l -i -# CHECK: login shell -# CHECK: interactive diff --git a/tests/checks/login.fish b/tests/checks/login.fish deleted file mode 100644 index b9965d866..000000000 --- a/tests/checks/login.fish +++ /dev/null @@ -1,3 +0,0 @@ -#RUN: %fish -c 'if status --is-login ; echo login shell ; else ; echo not login shell ; end; if status --is-interactive ; echo interactive ; else ; echo not interactive ; end' -l -# CHECK: login shell -# CHECK: not interactive diff --git a/tests/checks/no-login-no-interactive.fish b/tests/checks/no-login-no-interactive.fish deleted file mode 100644 index dd369e599..000000000 --- a/tests/checks/no-login-no-interactive.fish +++ /dev/null @@ -1,4 +0,0 @@ -#RUN: %fish -c 'if status --is-login ; echo login shell ; else ; echo not login shell ; end; if status --is-interactive ; echo interactive ; else ; echo not interactive ; end' - -# CHECK: not login shell -# CHECK: not interactive diff --git a/tests/checks/sigint.fish b/tests/checks/sigint.fish index 17916d6c2..bb645fd1d 100644 --- a/tests/checks/sigint.fish +++ b/tests/checks/sigint.fish @@ -1,5 +1,17 @@ #RUN: %fish -C "set helper %fish_test_helper" %s +# Block some signals if job control is off (#6828). + +status job-control none +for fish_use_posix_spawn in 0 1 + $helper print_blocked_signals & + wait +end +# CHECKERR: Interrupt: 2 +# CHECKERR: Quit: 3 +# CHECKERR: Interrupt: 2 +# CHECKERR: Quit: 3 + # Ensure we can break from a while loop. echo About to sigint diff --git a/tests/checks/string.fish b/tests/checks/string.fish index b3d598a49..518388fce 100644 --- a/tests/checks/string.fish +++ b/tests/checks/string.fish @@ -88,6 +88,32 @@ string split "" abc # CHECK: b # CHECK: c +string split --fields=2 "" abc +# CHECK: b + +string split --fields=3,2 "" abc +# CHECK: c +# CHECK: b + +string split --fields=2,9 "" abc; or echo "exit 1" +# CHECK: exit 1 + +string split --fields=1-3,5,9-7 "" 123456789 +# CHECK: 1 +# CHECK: 2 +# CHECK: 3 +# CHECK: 5 +# CHECK: 9 +# CHECK: 8 +# CHECK: 7 + +string split -f1 ' ' 'a b' 'c d' +# CHECK: a +# CHECK: c + +string split --allow-empty --fields=2,9 "" abc +# CHECK: b + seq 3 | string join ... # CHECK: 1...2...3 diff --git a/tests/signals.expect b/tests/signals.expect index 1cfc44f06..360ecfcb3 100644 --- a/tests/signals.expect +++ b/tests/signals.expect @@ -2,9 +2,18 @@ # # Test signal handling for interactive shells. -set pid [spawn $fish] +set pid [spawn $fish -C "sleep 10&"] expect_prompt +send "\x03" +sleep 0.010 +send_line "jobs" +expect_prompt -re {sleep.10} {} unmatched { + puts stderr "background job missing after SIGINT" +} +send_line "kill %1" +expect_prompt + # Verify that the fish_postexec handler is called after SIGINT. send_line "function postexec --on-event fish_postexec; echo fish_postexec spotted; end" expect_prompt @@ -12,6 +21,22 @@ send_line read expect -re "\\r\\n?read> $" exec -- kill -INT $pid expect "fish_postexec spotted" +expect_prompt + +# Verify that the fish_kill_signal is set. +send_line "functions -e postexec; function postexec --on-event fish_postexec; echo fish_kill_signal \$fish_kill_signal; end" +expect_prompt +send_line "sleep 5" +sleep 0.100 +exec -- pkill -INT sleep -P $pid +expect "fish_kill_signal 2" +expect_prompt + +send_line "sleep 5" +sleep 0.100 +exec -- pkill -TERM sleep -P $pid +expect "fish_kill_signal 15" +expect_prompt # Verify that sending SIGHUP to the shell, such as will happen when the tty is # closed by the terminal, terminates the shell and the foreground command and