mirror of
https://github.com/fish-shell/fish-shell.git
synced 2026-05-09 17:01:16 -03:00
Compare commits
7 Commits
Integratio
...
Integratio
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1e68508b0c | ||
|
|
2aac8e5dde | ||
|
|
209d8b7f2f | ||
|
|
26663e042f | ||
|
|
55986120aa | ||
|
|
aea9ad4965 | ||
|
|
216d32055d |
@@ -1,27 +0,0 @@
|
||||
image: alpine/edge
|
||||
packages:
|
||||
- cargo
|
||||
- clang17-libclang
|
||||
- cmake
|
||||
- ninja
|
||||
- pcre2-dev
|
||||
- py3-pexpect
|
||||
- python3
|
||||
- rust
|
||||
- tmux
|
||||
sources:
|
||||
- https://github.com/fish-shell/fish-shell
|
||||
tasks:
|
||||
- build: |
|
||||
cd fish-shell
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -G Ninja .. \
|
||||
-DCMAKE_INSTALL_PREFIX=/usr \
|
||||
-DCMAKE_INSTALL_DATADIR=share \
|
||||
-DCMAKE_INSTALL_DOCDIR=share/doc/fish \
|
||||
-DCMAKE_INSTALL_SYSCONFDIR=/etc
|
||||
ninja
|
||||
- test: |
|
||||
cd fish-shell/build
|
||||
ninja test
|
||||
@@ -1,23 +0,0 @@
|
||||
image: archlinux
|
||||
packages:
|
||||
- cmake
|
||||
- ninja
|
||||
- python
|
||||
- python-pexpect
|
||||
- tmux
|
||||
sources:
|
||||
- https://git.sr.ht/~faho/fish
|
||||
tasks:
|
||||
- build: |
|
||||
cd fish
|
||||
mkdir build || :
|
||||
cd build
|
||||
cmake -G Ninja .. \
|
||||
-DCMAKE_INSTALL_PREFIX=/usr \
|
||||
-DCMAKE_INSTALL_DATADIR=share \
|
||||
-DCMAKE_INSTALL_DOCDIR=share/doc/fish \
|
||||
-DCMAKE_INSTALL_SYSCONFDIR=/etc
|
||||
ninja
|
||||
- test: |
|
||||
cd fish/build
|
||||
ninja test
|
||||
@@ -1,30 +0,0 @@
|
||||
image: freebsd/latest
|
||||
packages:
|
||||
- cmake
|
||||
- gcc
|
||||
- gettext
|
||||
- gmake
|
||||
- llvm
|
||||
- terminfo-db
|
||||
- ninja
|
||||
- pcre2
|
||||
- py311-pexpect
|
||||
- python
|
||||
- rust
|
||||
- tmux
|
||||
sources:
|
||||
- https://github.com/fish-shell/fish-shell
|
||||
tasks:
|
||||
- build: |
|
||||
cd fish-shell
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -GNinja .. \
|
||||
-DCMAKE_INSTALL_PREFIX=/usr \
|
||||
-DCMAKE_INSTALL_DATADIR=share \
|
||||
-DCMAKE_INSTALL_DOCDIR=share/doc/fish \
|
||||
-DCMAKE_INSTALL_SYSCONFDIR=/etc
|
||||
ninja
|
||||
- test: |
|
||||
cd fish-shell/build
|
||||
ninja test
|
||||
@@ -1,8 +0,0 @@
|
||||
# This file is _not_ included in the tarballs for now
|
||||
# Binary builds on Linux packaging infrastructure need to overwrite it to make `cargo vendor` work
|
||||
# Releases and development builds made using OBS/Launchpad will _not_ reflect the contents of this
|
||||
# file
|
||||
|
||||
[resolver]
|
||||
# Make cargo 1.84+ respect MSRV (rust-version in Cargo.toml)
|
||||
incompatible-rust-versions = "fallback"
|
||||
77
.cirrus.yml
77
.cirrus.yml
@@ -1,77 +0,0 @@
|
||||
env:
|
||||
CIRRUS_CLONE_DEPTH: 100
|
||||
CI: 1
|
||||
|
||||
linux_task:
|
||||
matrix:
|
||||
- name: alpine
|
||||
container: &step
|
||||
image: ghcr.io/krobelus/fish-ci/alpine:latest
|
||||
memory: 4GB
|
||||
- name: jammy
|
||||
container:
|
||||
<<: *step
|
||||
image: ghcr.io/krobelus/fish-ci/jammy:latest
|
||||
# - name: jammy-asan
|
||||
# container:
|
||||
# <<: *step
|
||||
# image: ghcr.io/krobelus/fish-ci/jammy-asan:latest
|
||||
# - name: focal-32bit
|
||||
# container:
|
||||
# <<: *step
|
||||
# image: ghcr.io/krobelus/fish-ci/focal-32bit:latest
|
||||
tests_script:
|
||||
# cirrus at times gives us 32 procs and 2 GB of RAM
|
||||
# Unrestriced parallelism results in OOM
|
||||
- lscpu || true
|
||||
- (cat /proc/meminfo | grep MemTotal) || true
|
||||
- mkdir build && cd build
|
||||
- FISH_TEST_MAX_CONCURRENCY=6 cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug ..
|
||||
- ninja -j 6 fish
|
||||
- ninja fish_run_tests
|
||||
only_if: $CIRRUS_REPO_OWNER == 'fish-shell'
|
||||
|
||||
linux_arm_task:
|
||||
matrix:
|
||||
- name: focal-arm64
|
||||
arm_container:
|
||||
image: ghcr.io/fish-shell/fish-ci/focal-arm64
|
||||
- name: jammy-armv7-32bit
|
||||
arm_container:
|
||||
image: ghcr.io/fish-shell/fish-ci/jammy-armv7-32bit
|
||||
tests_script:
|
||||
# cirrus at times gives us 32 procs and 2 GB of RAM
|
||||
# Unrestriced parallelism results in OOM
|
||||
- lscpu || true
|
||||
- (cat /proc/meminfo | grep MemTotal) || true
|
||||
- mkdir build && cd build
|
||||
- FISH_TEST_MAX_CONCURRENCY=6 cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug ..
|
||||
- ninja -j 6 fish
|
||||
- file ./fish
|
||||
- ninja fish_run_tests
|
||||
# CI task disabled during RIIR transition
|
||||
only_if: false && $CIRRUS_REPO_OWNER == 'fish-shell'
|
||||
|
||||
freebsd_task:
|
||||
matrix:
|
||||
- name: FreeBSD 14
|
||||
freebsd_instance:
|
||||
image: freebsd-14-3-release-amd64-ufs
|
||||
tests_script:
|
||||
- pkg install -y cmake-core devel/pcre2 devel/ninja gettext git-lite lang/rust misc/py-pexpect
|
||||
# libclang.so is a required build dependency for rust-c++ ffi bridge
|
||||
- pkg install -y llvm
|
||||
# BSDs have the following behavior: root may open or access files even if
|
||||
# the mode bits would otherwise disallow it. For example root may open()
|
||||
# a file with write privileges even if the file has mode 400. This breaks
|
||||
# our tests for e.g. cd and path. So create a new unprivileged user to run tests.
|
||||
- pw user add -n fish-user -s /bin/csh -d /home/fish-user
|
||||
- mkdir -p /home/fish-user
|
||||
- chown -R fish-user /home/fish-user
|
||||
- mkdir build && cd build
|
||||
- chown -R fish-user ..
|
||||
- sudo -u fish-user -s whoami
|
||||
- sudo -u fish-user -s FISH_TEST_MAX_CONCURRENCY=1 cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug ..
|
||||
- sudo -u fish-user -s ninja -j 6 fish
|
||||
- sudo -u fish-user -s ninja fish_run_tests
|
||||
only_if: $CIRRUS_REPO_OWNER == 'fish-shell'
|
||||
@@ -1,8 +0,0 @@
|
||||
# Use the Google style with these modifications:
|
||||
#
|
||||
# 1) lines can be up to 100 chars long rather than 80, and
|
||||
# 2) use a four space indent rather than two spaces.
|
||||
#
|
||||
BasedOnStyle: Google
|
||||
ColumnLimit: 100
|
||||
IndentWidth: 4
|
||||
@@ -1,32 +0,0 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
max_line_length = 100
|
||||
|
||||
[{Makefile,*.in}]
|
||||
indent_style = tab
|
||||
|
||||
[*.{md,rst}]
|
||||
max_line_length = unset
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.sh]
|
||||
indent_size = 4
|
||||
|
||||
[build_tools/release.sh]
|
||||
max_line_length = 72
|
||||
|
||||
[Dockerfile]
|
||||
indent_size = 2
|
||||
|
||||
[share/{completions,functions}/**.fish]
|
||||
max_line_length = unset
|
||||
|
||||
[{COMMIT_EDITMSG,git-revise-todo,*.jjdescription}]
|
||||
max_line_length = 72
|
||||
30
.gitattributes
vendored
30
.gitattributes
vendored
@@ -1,32 +1,4 @@
|
||||
# normalize newlines
|
||||
* text=auto eol=lf
|
||||
|
||||
# let git show off diff hunk headers, help git diff -L:
|
||||
# https://git-scm.com/docs/gitattributes
|
||||
*.c diff=cpp
|
||||
*.h diff=cpp
|
||||
*.py diff=py
|
||||
*.rs diff=rust
|
||||
# add a [diff "fish"] to git config with pattern
|
||||
*.fish diff=fish
|
||||
|
||||
# omit from git archive
|
||||
.gitattributes export-ignore
|
||||
.gitignore export-ignore
|
||||
/build_tools/make_tarball.sh export-ignore
|
||||
/debian export-ignore
|
||||
/debian/* export-ignore
|
||||
/.github export-ignore
|
||||
/.github/* export-ignore
|
||||
/.builds export-ignore
|
||||
/.builds/* export-ignore
|
||||
# to make cargo vendor work correctly
|
||||
/.cargo/ export-ignore
|
||||
/.cargo/config.toml export-ignore
|
||||
/build_tools export-ignore
|
||||
|
||||
# for linguist, which drives GitHub's language statistics
|
||||
alpine.js linguist-vendored
|
||||
doc_src/** linguist-documentation
|
||||
*.fish linguist-language=fish
|
||||
# see 70f2899fcd which attempts to "rig the count"
|
||||
share/completions/*.fish linguist-documentation
|
||||
|
||||
24
.github/ISSUE_TEMPLATE/bug.md
vendored
24
.github/ISSUE_TEMPLATE/bug.md
vendored
@@ -1,24 +0,0 @@
|
||||
---
|
||||
name: "Bug Report"
|
||||
about: "Simple template for bug reports"
|
||||
title: ""
|
||||
labels: []
|
||||
assignees: []
|
||||
---
|
||||
|
||||
<!--
|
||||
Please tell us which fish version you are using by executing the following:
|
||||
|
||||
fish --version
|
||||
echo $version
|
||||
|
||||
Please tell us which operating system (output of `uname`) and terminal you are using.
|
||||
|
||||
Please tell us if you tried fish without third-party customizations by executing this command and whether it affected the behavior you are reporting:
|
||||
|
||||
sh -c 'env HOME=$(mktemp -d) XDG_CONFIG_HOME= XDG_DATA_DIRS= fish'
|
||||
|
||||
Tell us how to reproduce the problem. Including an asciinema.org recording is useful for problems that involve the visual display of fish output such as its prompt.
|
||||
-->
|
||||
|
||||
**YOUR TEXT HERE**
|
||||
11
.github/PULL_REQUEST_TEMPLATE.md
vendored
11
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,11 +0,0 @@
|
||||
## Description
|
||||
|
||||
Talk about your changes here.
|
||||
|
||||
Fixes issue #
|
||||
|
||||
## TODOs:
|
||||
<!-- Just check off what what we know been done so far. We can help you with this stuff. -->
|
||||
- [ ] Changes to fish usage are reflected in user documentation/manpages.
|
||||
- [ ] Tests have been added for regressions fixed
|
||||
- [ ] User-visible changes noted in CHANGELOG.rst <!-- Don't document changes for completions inside CHANGELOG.rst, there are lot of such edits -->
|
||||
@@ -1,14 +0,0 @@
|
||||
name: Install sphinx-markdown-builder
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- shell: bash
|
||||
run: |
|
||||
set -x
|
||||
commit=b259de1dc97573a71470a1d71c3d83535934136b
|
||||
pip install git+https://github.com/krobelus/sphinx-markdown-builder@"$commit"
|
||||
python -c 'import sphinx_markdown_builder'
|
||||
@@ -1,20 +0,0 @@
|
||||
name: Oldest Supported Rust Toolchain
|
||||
|
||||
inputs:
|
||||
targets:
|
||||
description: Comma-separated list of target triples to install for this toolchain
|
||||
required: false
|
||||
components:
|
||||
description: Comma-separated list of components to be additionally installed
|
||||
required: false
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- uses: dtolnay/rust-toolchain@1.70
|
||||
with:
|
||||
targets: ${{ inputs.targets }}
|
||||
components: ${{ inputs.components}}
|
||||
20
.github/actions/rust-toolchain@stable/action.yml
vendored
20
.github/actions/rust-toolchain@stable/action.yml
vendored
@@ -1,20 +0,0 @@
|
||||
name: Stable Rust Toolchain
|
||||
|
||||
inputs:
|
||||
targets:
|
||||
description: Comma-separated list of target triples to install for this toolchain
|
||||
required: false
|
||||
components:
|
||||
description: Comma-separated list of components to be additionally installed
|
||||
required: false
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- uses: dtolnay/rust-toolchain@1.90
|
||||
with:
|
||||
targets: ${{ inputs.targets }}
|
||||
components: ${{ inputs.components }}
|
||||
40
.github/workflows/autolabel_prs.yml
vendored
40
.github/workflows/autolabel_prs.yml
vendored
@@ -1,40 +0,0 @@
|
||||
name: Auto-Label PRs
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened, synchronize]
|
||||
|
||||
jobs:
|
||||
label-and-milestone:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set label and milestone
|
||||
id: set-label-milestone
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const completionsLabel = 'completions';
|
||||
|
||||
// Get changed files in the pull request
|
||||
const prNumber = context.payload.pull_request.number;
|
||||
const { data: files } = await github.rest.pulls.listFiles({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: prNumber,
|
||||
});
|
||||
|
||||
// Check if any file matches /share/completions/*.fish and no change is outside of /share/
|
||||
const completionsRegex = new RegExp('^share/completions/.*\.fish');
|
||||
const isCompletions = files.some(file => completionsRegex.test(file.filename))
|
||||
&& files.every(file => file.filename.startsWith('share/'));
|
||||
|
||||
if (isCompletions) {
|
||||
// Add label to PR
|
||||
await github.rest.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
labels: [completionsLabel],
|
||||
});
|
||||
console.log(`PR ${prNumber} assigned label "${completionsLabel}"`);
|
||||
}
|
||||
25
.github/workflows/lockthreads.yml
vendored
25
.github/workflows/lockthreads.yml
vendored
@@ -1,25 +0,0 @@
|
||||
name: 'Lock threads'
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 18 * * 1'
|
||||
# │ │ │ │ │
|
||||
# min 0-59 ┘ │ │ │ └ weekday 0-6
|
||||
# hour 0-23 ┘ │ └ month 1-12
|
||||
# └ day 1-31
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
lock:
|
||||
permissions:
|
||||
issues: write # for dessant/lock-threads to lock issues
|
||||
pull-requests: write # for dessant/lock-threads to lock PRs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: dessant/lock-threads@v4
|
||||
with:
|
||||
github-token: ${{ github.token }}
|
||||
issue-inactive-days: '365'
|
||||
pr-inactive-days: '365'
|
||||
exclude-any-issue-labels: 'question, needs more info'
|
||||
176
.github/workflows/main.yml
vendored
176
.github/workflows/main.yml
vendored
@@ -1,176 +0,0 @@
|
||||
name: make fish_run_tests
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
env:
|
||||
FISH_TEST_MAX_CONCURRENCY: "4"
|
||||
CMAKE_BUILD_PARALLEL_LEVEL: "4"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
ubuntu:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/rust-toolchain@oldest-supported
|
||||
- name: Install deps
|
||||
run: |
|
||||
sudo apt install gettext libpcre2-dev python3-pexpect python3-sphinx tmux
|
||||
# Generate a locale that uses a comma as decimal separator.
|
||||
sudo locale-gen fr_FR.UTF-8
|
||||
- uses: ./.github/actions/install-sphinx-markdown-builder
|
||||
- name: cmake
|
||||
run: |
|
||||
mkdir build && cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo
|
||||
- name: make
|
||||
run: |
|
||||
make -C build VERBOSE=1
|
||||
- name: make fish_run_tests
|
||||
run: |
|
||||
make -C build VERBOSE=1 fish_run_tests
|
||||
- name: translation updates
|
||||
run: |
|
||||
# Generate PO files. This should not result it a change in the repo if all translations are
|
||||
# up to date.
|
||||
# Ensure that fish is available as an executable.
|
||||
PATH="$PWD/build:$PATH" build_tools/update_translations.fish
|
||||
# Show diff output. Fail if there is any.
|
||||
git --no-pager diff --exit-code || { echo 'There are uncommitted changes after regenerating the gettext PO files. Make sure to update them via `build_tools/update_translations.fish` after changing source files.'; exit 1; }
|
||||
|
||||
ubuntu-32bit-static-pcre2:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/rust-toolchain@oldest-supported
|
||||
with:
|
||||
targets: "i586-unknown-linux-gnu" # rust-toolchain wants this comma-separated
|
||||
- name: Install deps
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install gettext python3-pexpect g++-multilib tmux
|
||||
- name: cmake
|
||||
env:
|
||||
CFLAGS: "-m32"
|
||||
run: |
|
||||
mkdir build && cd build
|
||||
cmake -DFISH_USE_SYSTEM_PCRE2=OFF -DRust_CARGO_TARGET=i586-unknown-linux-gnu ..
|
||||
- name: make
|
||||
run: |
|
||||
make -C build VERBOSE=1
|
||||
- name: make fish_run_tests
|
||||
run: |
|
||||
make -C build VERBOSE=1 fish_run_tests
|
||||
|
||||
ubuntu-asan:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
# Rust has two different memory sanitizers of interest; they can't be used at the same time:
|
||||
# * AddressSanitizer detects out-of-bound access, use-after-free, use-after-return,
|
||||
# use-after-scope, double-free, invalid-free, and memory leaks.
|
||||
# * MemorySanitizer detects uninitialized reads.
|
||||
#
|
||||
RUSTFLAGS: "-Zsanitizer=address"
|
||||
# RUSTFLAGS: "-Zsanitizer=memory -Zsanitizer-memory-track-origins"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
# All -Z options require running nightly
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
with:
|
||||
# ASAN uses `cargo build -Zbuild-std` which requires the rust-src component
|
||||
# this is comma-separated
|
||||
components: rust-src
|
||||
- name: Install deps
|
||||
run: |
|
||||
sudo apt install gettext libpcre2-dev python3-pexpect tmux
|
||||
sudo apt install llvm # for llvm-symbolizer
|
||||
- name: cmake
|
||||
env:
|
||||
CC: clang
|
||||
run: |
|
||||
mkdir build && cd build
|
||||
# Rust's ASAN requires the build system to explicitly pass a --target triple. We read that
|
||||
# value from CMake variable Rust_CARGO_TARGET.
|
||||
cmake .. -DASAN=1 -DRust_CARGO_TARGET=x86_64-unknown-linux-gnu -DCMAKE_BUILD_TYPE=Debug
|
||||
- name: make
|
||||
run: |
|
||||
make -C build VERBOSE=1
|
||||
- name: make fish_run_tests
|
||||
env:
|
||||
FISH_CI_SAN: 1
|
||||
ASAN_OPTIONS: check_initialization_order=1:detect_stack_use_after_return=1:detect_leaks=1:fast_unwind_on_malloc=0
|
||||
# use_tls=0 is a workaround for LSAN crashing with "Tracer caught signal 11" (SIGSEGV),
|
||||
# which seems to be an issue with TLS support in newer glibc versions under virtualized
|
||||
# environments. Follow https://github.com/google/sanitizers/issues/1342 and
|
||||
# https://github.com/google/sanitizers/issues/1409 to track this issue.
|
||||
# UPDATE: this can cause spurious leak reports for __cxa_thread_atexit_impl() under glibc.
|
||||
LSAN_OPTIONS: verbosity=0:log_threads=0:use_tls=1:print_suppressions=0
|
||||
run: |
|
||||
set -x
|
||||
export ASAN_SYMBOLIZER_PATH=$(command -v /usr/bin/llvm-symbolizer* | sort -n | head -1)
|
||||
export LSAN_OPTIONS="$LSAN_OPTIONS:suppressions=$PWD/build_tools/lsan_suppressions.txt"
|
||||
make -C build VERBOSE=1 fish_run_tests
|
||||
|
||||
# Our clang++ tsan builds are not recognizing safe rust patterns (such as the fact that Drop
|
||||
# cannot be called while a thread is using the object in question). Rust has its own way of
|
||||
# running TSAN, but for the duration of the port from C++ to Rust, we'll keep this disabled.
|
||||
|
||||
# ubuntu-threadsan:
|
||||
#
|
||||
# runs-on: ubuntu-latest
|
||||
#
|
||||
# steps:
|
||||
# - uses: actions/checkout@v4
|
||||
# - uses: ./.github/actions/rust-toolchain@oldest-supported
|
||||
# - name: Install deps
|
||||
# run: |
|
||||
# sudo apt install gettext libpcre2-dev python3-pexpect tmux
|
||||
# - name: cmake
|
||||
# env:
|
||||
# FISH_CI_SAN: 1
|
||||
# CC: clang
|
||||
# run: |
|
||||
# mkdir build && cd build
|
||||
# cmake ..
|
||||
# - name: make
|
||||
# run: |
|
||||
# make
|
||||
# - name: make fish_run_tests
|
||||
# run: |
|
||||
# make -C build fish_run_tests
|
||||
|
||||
macos:
|
||||
|
||||
runs-on: macos-latest
|
||||
|
||||
env:
|
||||
# macOS runners keep having issues loading Cargo.toml dependencies from git (GitHub) instead
|
||||
# of crates.io, so give this a try. It's also sometimes significantly faster on all platforms.
|
||||
CARGO_NET_GIT_FETCH_WITH_CLI: true
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/rust-toolchain@oldest-supported
|
||||
- name: Install deps
|
||||
run: |
|
||||
# --break-system-packages because homebrew has now declared itself "externally managed".
|
||||
# this is CI so we don't actually care.
|
||||
sudo pip3 install --break-system-packages pexpect
|
||||
brew install tmux
|
||||
- name: cmake
|
||||
run: |
|
||||
mkdir build && cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Debug ..
|
||||
- name: make
|
||||
run: |
|
||||
make -C build VERBOSE=1
|
||||
- name: make fish_run_tests
|
||||
run: |
|
||||
make -C build VERBOSE=1 fish_run_tests
|
||||
189
.github/workflows/release.yml
vendored
189
.github/workflows/release.yml
vendored
@@ -1,189 +0,0 @@
|
||||
name: Create a new release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'Version to release (tag name)'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
is-release-tag:
|
||||
name: Pre-release checks
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
# Workaround for https://github.com/actions/checkout/issues/882
|
||||
ref: ${{ inputs.version }}
|
||||
- name: Check if the pushed tag looks like a release
|
||||
run: |
|
||||
set -x
|
||||
commit_subject=$(git log -1 --format=%s)
|
||||
tag=$(git describe)
|
||||
[ "$commit_subject" = "Release $tag" ]
|
||||
|
||||
|
||||
source-tarball:
|
||||
needs: [is-release-tag]
|
||||
name: Create the source tarball
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
version: ${{ steps.version.outputs.version }}
|
||||
tarball-name: ${{ steps.version.outputs.tarball-name }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
# Workaround for https://github.com/actions/checkout/issues/882
|
||||
ref: ${{ inputs.version }}
|
||||
- name: Install dependencies
|
||||
run: sudo apt install cmake gettext ninja-build python3-pip python3-sphinx
|
||||
- uses: ./.github/actions/install-sphinx-markdown-builder
|
||||
- name: Create tarball
|
||||
run: |
|
||||
set -x
|
||||
mkdir /tmp/fish-built
|
||||
FISH_ARTEFACT_PATH=/tmp/fish-built ./build_tools/make_tarball.sh
|
||||
relnotes=/tmp/fish-built/release-notes.md
|
||||
# Need history since the last release (i.e. tag) for stats.
|
||||
git fetch --tags
|
||||
git fetch --unshallow
|
||||
sh -x ./build_tools/release-notes.sh >"$relnotes"
|
||||
# Delete title
|
||||
sed -n 1p "$relnotes" | grep -q "^## fish .*"
|
||||
sed -n 2p "$relnotes" | grep -q '^$'
|
||||
sed -i 1,2d "$relnotes"
|
||||
- name: Upload tarball artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: source-tarball
|
||||
path: |
|
||||
/tmp/fish-built/fish-${{ inputs.version }}.tar.xz
|
||||
/tmp/fish-built/release-notes.md
|
||||
if-no-files-found: error
|
||||
|
||||
packages-for-linux:
|
||||
needs: [is-release-tag]
|
||||
name: Build single-file fish for Linux (experimental)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
# Workaround for https://github.com/actions/checkout/issues/882
|
||||
ref: ${{ inputs.version }}
|
||||
- name: Install Rust Stable
|
||||
uses: ./.github/actions/rust-toolchain@stable
|
||||
with:
|
||||
targets: x86_64-unknown-linux-musl,aarch64-unknown-linux-musl
|
||||
- name: Install dependencies
|
||||
run: sudo apt install crossbuild-essential-arm64 gettext musl-tools python3-sphinx
|
||||
- name: Build statically-linked executables
|
||||
run: |
|
||||
set -x
|
||||
cargo build --release --target x86_64-unknown-linux-musl --bin fish
|
||||
CFLAGS="-D_FORTIFY_SOURCE=2" \
|
||||
CC=aarch64-linux-gnu-gcc \
|
||||
RUSTFLAGS="-C linker=aarch64-linux-gnu-gcc -C link-arg=-lgcc -C link-arg=-D_FORTIFY_SOURCE=0" \
|
||||
cargo build --release --target aarch64-unknown-linux-musl --bin fish
|
||||
- name: Compress
|
||||
run: |
|
||||
set -x
|
||||
for arch in x86_64 aarch64; do
|
||||
tar -cazf fish-$(git describe)-linux-$arch.tar.xz \
|
||||
-C target/$arch-unknown-linux-musl/release fish
|
||||
done
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Static builds for Linux
|
||||
path: fish-${{ inputs.version }}-linux-*.tar.xz
|
||||
if-no-files-found: error
|
||||
|
||||
create-draft-release:
|
||||
needs:
|
||||
- is-release-tag
|
||||
- source-tarball
|
||||
- packages-for-linux
|
||||
name: Create release draft
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
# Workaround for https://github.com/actions/checkout/issues/882
|
||||
ref: ${{ inputs.version }}
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
merge-multiple: true
|
||||
path: /tmp/artifacts
|
||||
- name: List artifacts
|
||||
run: find /tmp/artifacts -type f
|
||||
- name: Create draft release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
tag_name: ${{ inputs.version }}
|
||||
name: fish ${{ inputs.version }}
|
||||
body_path: /tmp/artifacts/release-notes.md
|
||||
draft: true
|
||||
files: |
|
||||
/tmp/artifacts/fish-${{ inputs.version }}.tar.xz
|
||||
/tmp/artifacts/fish-${{ inputs.version }}-linux-*.tar.xz
|
||||
|
||||
packages-for-macos:
|
||||
needs: [is-release-tag, create-draft-release]
|
||||
name: Build packages for macOS
|
||||
runs-on: macos-latest
|
||||
environment: macos-codesign
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
# Workaround for https://github.com/actions/checkout/issues/882
|
||||
ref: ${{ inputs.version }}
|
||||
- name: Install Rust
|
||||
uses: ./.github/actions/rust-toolchain@oldest-supported
|
||||
with:
|
||||
targets: x86_64-apple-darwin
|
||||
- name: Install Rust Stable
|
||||
uses: ./.github/actions/rust-toolchain@stable
|
||||
with:
|
||||
targets: aarch64-apple-darwin
|
||||
- name: Build and codesign
|
||||
run: |
|
||||
die() { echo >&2 "$*"; exit 1; }
|
||||
[ -n "$MAC_CODESIGN_APP_P12_BASE64" ] || die "Missing MAC_CODESIGN_APP_P12_BASE64"
|
||||
[ -n "$MAC_CODESIGN_INSTALLER_P12_BASE64" ] || die "Missing MAC_CODESIGN_INSTALLER_P12_BASE64"
|
||||
[ -n "$MAC_CODESIGN_PASSWORD" ] || die "Missing MAC_CODESIGN_PASSWORD"
|
||||
[ -n "$MACOS_NOTARIZE_JSON" ] || die "Missing MACOS_NOTARIZE_JSON"
|
||||
set -x
|
||||
export FISH_ARTEFACT_PATH=/tmp/fish-built
|
||||
# macOS runners keep having issues loading Cargo.toml dependencies from git (GitHub) instead
|
||||
# of crates.io, so give this a try. It's also sometimes significantly faster on all platforms.
|
||||
export CARGO_NET_GIT_FETCH_WITH_CLI=true
|
||||
cargo install apple-codesign
|
||||
mkdir -p "$FISH_ARTEFACT_PATH"
|
||||
echo "$MAC_CODESIGN_APP_P12_BASE64" | base64 --decode >/tmp/app.p12
|
||||
echo "$MAC_CODESIGN_INSTALLER_P12_BASE64" | base64 --decode >/tmp/installer.p12
|
||||
echo "$MACOS_NOTARIZE_JSON" >/tmp/notarize.json
|
||||
./build_tools/make_macos_pkg.sh -s -f /tmp/app.p12 \
|
||||
-i /tmp/installer.p12 -p "$MAC_CODESIGN_PASSWORD" \
|
||||
-n -j /tmp/notarize.json
|
||||
version=$(git describe)
|
||||
[ -f "${FISH_ARTEFACT_PATH}/fish-$version.app.zip" ]
|
||||
[ -f "${FISH_ARTEFACT_PATH}/fish-$version.pkg" ]
|
||||
rm /tmp/installer.p12 /tmp/app.p12 /tmp/notarize.json
|
||||
env:
|
||||
MAC_CODESIGN_APP_P12_BASE64: ${{ secrets.MAC_CODESIGN_APP_P12_BASE64 }}
|
||||
MAC_CODESIGN_INSTALLER_P12_BASE64: ${{ secrets.MAC_CODESIGN_INSTALLER_P12_BASE64 }}
|
||||
MAC_CODESIGN_PASSWORD: ${{ secrets.MAC_CODESIGN_PASSWORD }}
|
||||
MACOS_NOTARIZE_JSON: ${{ secrets.MACOS_NOTARIZE_JSON }}
|
||||
- name: Add macOS packages to the release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
version=$(git describe)
|
||||
gh release upload $version \
|
||||
/tmp/fish-built/fish-$version.app.zip \
|
||||
/tmp/fish-built/fish-$version.pkg
|
||||
61
.github/workflows/rust_checks.yml
vendored
61
.github/workflows/rust_checks.yml
vendored
@@ -1,61 +0,0 @@
|
||||
name: Rust checks
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
rustfmt:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/rust-toolchain@stable
|
||||
with:
|
||||
components: rustfmt
|
||||
- name: cargo fmt
|
||||
run: cargo fmt --check
|
||||
|
||||
clippy-stable:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
features: ["", "--no-default-features"]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/rust-toolchain@stable
|
||||
with:
|
||||
components: clippy
|
||||
- name: Install deps
|
||||
run: |
|
||||
sudo apt install gettext
|
||||
- name: cargo clippy
|
||||
run: cargo clippy --workspace --all-targets ${{ matrix.features }} -- --deny=warnings
|
||||
|
||||
clippy-msrv:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/rust-toolchain@oldest-supported
|
||||
with:
|
||||
components: clippy
|
||||
- name: Install deps
|
||||
run: |
|
||||
sudo apt install gettext
|
||||
- name: cargo clippy
|
||||
run: cargo clippy --workspace --all-targets -- --deny=warnings
|
||||
|
||||
rustdoc:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/rust-toolchain@stable
|
||||
- name: Install deps
|
||||
run: |
|
||||
sudo apt install gettext
|
||||
- name: cargo doc
|
||||
run: |
|
||||
RUSTDOCFLAGS='-D warnings' cargo doc --workspace
|
||||
- name: cargo doctest
|
||||
run: |
|
||||
cargo test --doc --workspace
|
||||
135
.gitignore
vendored
135
.gitignore
vendored
@@ -1,107 +1,34 @@
|
||||
# Note that some of the patterns below should be in an individual's
|
||||
# ~/.config/git/ignore file. For example, ".DS_Store" from people working on
|
||||
# MacOS.
|
||||
|
||||
# File extensions that should never be checked in regardless of which project
|
||||
# directory they reside in.
|
||||
*.DS_Store
|
||||
*.a
|
||||
*.app
|
||||
*.d
|
||||
*.dll
|
||||
*.dylib
|
||||
*.exe
|
||||
*.gch
|
||||
*.la
|
||||
*.lai
|
||||
*.lib
|
||||
*.lo
|
||||
*.log
|
||||
*.new
|
||||
*.o
|
||||
*.obj
|
||||
*.orig
|
||||
!tests/*.out
|
||||
*.out
|
||||
*.pch
|
||||
*.slo
|
||||
*.so
|
||||
*.xccheckout
|
||||
*bak
|
||||
*~
|
||||
*~HEAD
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
.Trash-*
|
||||
._*
|
||||
Desktop.ini
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
|
||||
.directory
|
||||
.fuse_hidden*
|
||||
|
||||
|
||||
# Directories that only contain transitory files from building and testing.
|
||||
/doc/
|
||||
/share/man/
|
||||
/share/doc/
|
||||
/test/
|
||||
/user_doc/
|
||||
|
||||
# File names that can appear in the project root that represent artifacts from
|
||||
# building and testing.
|
||||
/FISH-BUILD-VERSION-FILE
|
||||
/command_list.txt
|
||||
/command_list_toc.txt
|
||||
/compile_commands.json
|
||||
/doc.h
|
||||
/fish
|
||||
/fish.pc
|
||||
/fish_indent
|
||||
/fish_key_reader
|
||||
/fish_tests
|
||||
/lexicon.txt
|
||||
/lexicon_filter
|
||||
/toc.txt
|
||||
/version
|
||||
fish-build-version-witness.txt
|
||||
__pycache__
|
||||
|
||||
# File names that can appear below the project root that represent artifacts
|
||||
# from building and testing.
|
||||
/doc_src/commands.hdr
|
||||
/doc_src/index.hdr
|
||||
/po/*.gmo
|
||||
/share/__fish_build_paths.fish
|
||||
/share/pkgconfig
|
||||
/tests/*.tmp.*
|
||||
/tests/.last-check-all-files
|
||||
|
||||
# xcode
|
||||
## Build generated
|
||||
*.moved-aside
|
||||
*.xccheckout
|
||||
*.xcscmblueprin
|
||||
.vscode
|
||||
/DerivedData/
|
||||
/build/
|
||||
/tags
|
||||
xcuserdata/
|
||||
|
||||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
debug/
|
||||
target/
|
||||
|
||||
# These are backup files generated by rustfmt
|
||||
**/*.rs.bk
|
||||
|
||||
# MSVC Windows builds of rustc generate these, which store debugging information
|
||||
*.pdb
|
||||
|
||||
# Generated by clangd
|
||||
/.cache
|
||||
|
||||
# JetBrains editors.
|
||||
.idea/
|
||||
Doxyfile.help
|
||||
Makefile
|
||||
autom4te.cache/
|
||||
build/
|
||||
command_list.txt
|
||||
confdefs.h
|
||||
config.h
|
||||
config.h.in
|
||||
config.log
|
||||
config.status
|
||||
configure
|
||||
doc.h
|
||||
doc_src/commands.hdr
|
||||
doc_src/index.hdr
|
||||
po/*.gmo
|
||||
fish
|
||||
fish.spec
|
||||
fish_indent
|
||||
fish_pager
|
||||
fish_tests
|
||||
fishd
|
||||
mimedb
|
||||
seq
|
||||
set_color
|
||||
share/config.fish
|
||||
share/man/
|
||||
toc.txt
|
||||
user_doc/
|
||||
xcuserdata
|
||||
tests/*tmp.*
|
||||
tests/foo.txt
|
||||
|
||||
59
BSDmakefile
59
BSDmakefile
@@ -1,59 +0,0 @@
|
||||
# This is a very basic `make` wrapper around the CMake build toolchain.
|
||||
#
|
||||
# Supported arguments:
|
||||
# PREFIX: sets the installation prefix
|
||||
# GENERATOR: explicitly specifies the CMake generator to use
|
||||
|
||||
# By default, bmake will try to cd into ./obj before anything else. Don't do that.
|
||||
.OBJDIR: ./
|
||||
|
||||
CMAKE?=cmake
|
||||
|
||||
# Before anything else, test for CMake, which is the only requirement to be able to run
|
||||
# this Makefile CMake will perform the remaining dependency tests on its own.
|
||||
.BEGIN:
|
||||
@which $(CMAKE) >/dev/null 2>/dev/null || \
|
||||
(echo 'Please install CMake and then re-run the `make` command!' 1>&2 && false)
|
||||
|
||||
# Prefer to use ninja, if it is installed
|
||||
_GENERATOR!=which ninja 2>/dev/null >/dev/null && echo Ninja || echo "Unix Makefiles"
|
||||
GENERATOR?=$(_GENERATOR)
|
||||
|
||||
.if $(GENERATOR) == "Ninja"
|
||||
BUILDFILE=build.ninja
|
||||
.else
|
||||
BUILDFILE=Makefile
|
||||
.endif
|
||||
|
||||
PREFIX?=/usr/local
|
||||
|
||||
.PHONY: build/fish
|
||||
build/fish: build/$(BUILDFILE)
|
||||
$(CMAKE) --build build
|
||||
|
||||
# Don't split the mkdir into its own rule because that would cause CMake to regenerate the build
|
||||
# files after each build (because it adds the mdate of the build directory into the out-of-date
|
||||
# calculation tree). GNUmake supports order-only dependencies, BSDmake does not seem to.
|
||||
build/$(BUILDFILE):
|
||||
mkdir -p build
|
||||
cd build; $(CMAKE) .. -G "$(GENERATOR)" -DCMAKE_INSTALL_PREFIX="$(PREFIX)" -DCMAKE_EXPORT_COMPILE_COMMANDS=1
|
||||
|
||||
.PHONY: install
|
||||
install: build/fish
|
||||
$(CMAKE) --build build --target install
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -rf build
|
||||
|
||||
.PHONY: test
|
||||
test: build/fish
|
||||
$(CMAKE) --build build --target fish_run_tests
|
||||
|
||||
.PHONY: fish_run_tests
|
||||
fish_run_tests: build/fish
|
||||
$(CMAKE) --build build --target fish_run_tests
|
||||
|
||||
.PHONY: run
|
||||
run: build/fish
|
||||
build/fish
|
||||
3
CHANGELOG
Normal file
3
CHANGELOG
Normal file
@@ -0,0 +1,3 @@
|
||||
24-01-2012 Jan Kanis
|
||||
* Added a changelog file
|
||||
* removed unescaping if the 'commandline' builtin is called without the -o (tokenise) flag
|
||||
4572
CHANGELOG.rst
4572
CHANGELOG.rst
File diff suppressed because it is too large
Load Diff
@@ -1,90 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.15)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
|
||||
|
||||
project(fish LANGUAGES C)
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
set(DEFAULT_BUILD_TYPE "RelWithDebInfo")
|
||||
|
||||
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
||||
message(STATUS "Setting build type to default '${DEFAULT_BUILD_TYPE}'")
|
||||
set(CMAKE_BUILD_TYPE "${DEFAULT_BUILD_TYPE}")
|
||||
endif()
|
||||
|
||||
# Set up standard directories.
|
||||
include(GNUInstallDirs)
|
||||
|
||||
# Set up PCRE2
|
||||
# This sets an environment variable that needs to be available before the Rust stanzas
|
||||
include(cmake/PCRE2.cmake)
|
||||
|
||||
include(cmake/Rust.cmake)
|
||||
|
||||
# Work around issue where archive-built libs go in the wrong place.
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
|
||||
|
||||
# Set up the machinery around FISH-BUILD-VERSION-FILE
|
||||
# This defines the FBVF variable.
|
||||
include(Version)
|
||||
|
||||
# Let fish pick up when we're running out of the build directory without installing
|
||||
get_filename_component(REAL_CMAKE_BINARY_DIR "${CMAKE_BINARY_DIR}" REALPATH)
|
||||
get_filename_component(REAL_CMAKE_SOURCE_DIR "${CMAKE_SOURCE_DIR}" REALPATH)
|
||||
add_definitions(-DCMAKE_BINARY_DIR="${REAL_CMAKE_BINARY_DIR}")
|
||||
add_definitions(-DCMAKE_SOURCE_DIR="${REAL_CMAKE_SOURCE_DIR}")
|
||||
|
||||
set(build_types Release RelWithDebInfo Debug "")
|
||||
if(NOT "${CMAKE_BUILD_TYPE}" IN_LIST build_types)
|
||||
message(WARNING "Unsupported build type ${CMAKE_BUILD_TYPE}. If this doesn't build, try one of Release, RelWithDebInfo or Debug")
|
||||
endif()
|
||||
|
||||
# Define a function to build and link dependencies.
|
||||
function(CREATE_TARGET target)
|
||||
add_custom_target(
|
||||
${target} ALL
|
||||
COMMAND
|
||||
"${CMAKE_COMMAND}" -E
|
||||
env ${VARS_FOR_CARGO}
|
||||
${Rust_CARGO}
|
||||
build --bin ${target}
|
||||
$<$<CONFIG:Release>:--release>
|
||||
$<$<CONFIG:RelWithDebInfo>:--profile=release-with-debug>
|
||||
--target ${Rust_CARGO_TARGET}
|
||||
--no-default-features
|
||||
--features=${FISH_CARGO_FEATURES}
|
||||
${CARGO_FLAGS}
|
||||
&&
|
||||
"${CMAKE_COMMAND}" -E
|
||||
copy "${rust_target_dir}/${rust_profile}/${target}" "${CMAKE_CURRENT_BINARY_DIR}"
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
USES_TERMINAL
|
||||
)
|
||||
endfunction(CREATE_TARGET)
|
||||
|
||||
# Define fish.
|
||||
create_target(fish)
|
||||
|
||||
# Define fish_indent.
|
||||
create_target(fish_indent)
|
||||
|
||||
# Define fish_key_reader.
|
||||
create_target(fish_key_reader)
|
||||
|
||||
# Set up the docs.
|
||||
include(cmake/Docs.cmake)
|
||||
|
||||
# Set up tests.
|
||||
include(cmake/Tests.cmake)
|
||||
|
||||
# Benchmarking support.
|
||||
include(cmake/Benchmark.cmake)
|
||||
|
||||
# Set up install.
|
||||
include(cmake/Install.cmake)
|
||||
|
||||
# Mac app.
|
||||
include(cmake/MacApp.cmake)
|
||||
|
||||
include(FeatureSummary)
|
||||
feature_summary(WHAT ALL)
|
||||
@@ -1,128 +0,0 @@
|
||||
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, religion, or sexual identity
|
||||
and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the
|
||||
overall community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or
|
||||
advances of any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or email
|
||||
address, without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series
|
||||
of actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or
|
||||
permanent ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within
|
||||
the community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.0, available at
|
||||
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||
|
||||
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||
enforcement ladder](https://github.com/mozilla/diversity).
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
https://www.contributor-covenant.org/faq. Translations are available at
|
||||
https://www.contributor-covenant.org/translations.
|
||||
414
CONTRIBUTING.rst
414
CONTRIBUTING.rst
@@ -1,414 +0,0 @@
|
||||
####################
|
||||
Contributing To Fish
|
||||
####################
|
||||
|
||||
This document tells you how you can contribute to fish.
|
||||
|
||||
Fish is free and open source software, distributed under the terms of the GPLv2.
|
||||
|
||||
Contributions are welcome, and there are many ways to contribute!
|
||||
|
||||
Whether you want to change some of the core Rust source, enhance or add a completion script or function,
|
||||
improve the documentation or translate something, this document will tell you how.
|
||||
|
||||
|
||||
Mailing List
|
||||
============
|
||||
|
||||
Send patches to the public mailing list: mailto:~krobelus/fish-shell@lists.sr.ht.
|
||||
Archives are available at https://lists.sr.ht/~krobelus/fish-shell/.
|
||||
|
||||
GitHub
|
||||
======
|
||||
|
||||
Fish is available on Github, at https://github.com/fish-shell/fish-shell.
|
||||
|
||||
First, you'll need an account there, and you'll need a git clone of fish.
|
||||
Fork it on Github and then run::
|
||||
|
||||
git clone https://github.com/<USERNAME>/fish-shell.git
|
||||
|
||||
This will create a copy of the fish repository in the directory fish-shell in your current working directory.
|
||||
|
||||
Also, for most changes you want to run the tests and so you'd get a setup to compile fish.
|
||||
For that, you'll require:
|
||||
|
||||
- Rust - when in doubt, try rustup
|
||||
- CMake
|
||||
- PCRE2 (headers and libraries) - optional, this will be downloaded if missing
|
||||
- gettext (only the msgfmt tool) - optional, for translation support
|
||||
- Sphinx - optional, to build the documentation
|
||||
|
||||
Of course not everything is required always - if you just want to contribute something to the documentation you'll just need Sphinx,
|
||||
and if the change is very simple and obvious you can just send it in. Use your judgement!
|
||||
|
||||
Once you have your changes, open a pull request on https://github.com/fish-shell/fish-shell/pulls.
|
||||
|
||||
Guidelines
|
||||
==========
|
||||
|
||||
In short:
|
||||
|
||||
- Be conservative in what you need (keep to the agreed minimum supported Rust version, limit new dependencies)
|
||||
- Use automated tools to help you (``build_tools/check.sh``)
|
||||
|
||||
Contributing completions
|
||||
========================
|
||||
|
||||
Completion scripts are the most common contribution to fish, and they are very welcome.
|
||||
|
||||
In general, we'll take all well-written completion scripts for a command that is publicly available.
|
||||
This means no private tools or personal scripts, and we do reserve the right to reject for other reasons.
|
||||
|
||||
Before you try to contribute them to fish, consider if the authors of the tool you are completing want to maintain the script instead.
|
||||
Often that makes more sense, specifically because they can add new options to the script immediately once they add them,
|
||||
and don't have to maintain one completion script for multiple versions. If the authors no longer wish to maintain the script,
|
||||
they can of course always contact the fish maintainers to hand it over, preferably by opening a PR.
|
||||
This isn't a requirement - if the authors don't want to maintain it, or you simply don't want to contact them,
|
||||
you can contribute your script to fish.
|
||||
|
||||
Completion scripts should
|
||||
|
||||
1. Use as few dependencies as possible - try to use fish's builtins like ``string`` instead of ``grep`` and ``awk``,
|
||||
use ``python`` to read json instead of ``jq`` (because it's already a soft dependency for fish's tools)
|
||||
2. If it uses a common unix tool, use posix-compatible invocations - ideally it would work on GNU/Linux, macOS, the BSDs and other systems
|
||||
3. Option and argument descriptions should be kept short.
|
||||
The shorter the description, the more likely it is that fish can use more columns.
|
||||
4. Function names should start with ``__fish``, and functions should be kept in the completion file unless they're used elsewhere.
|
||||
5. Run ``fish_indent`` on your script.
|
||||
6. Try not to use minor convenience features right after they are available in fish - we do try to keep completion scripts backportable.
|
||||
If something has a real impact on the correctness or performance, feel free to use it,
|
||||
but if it is just a shortcut, please leave it.
|
||||
|
||||
Put your completion script into share/completions/name-of-command.fish. If you have multiple commands, you need multiple files.
|
||||
|
||||
If you want to add tests, you probably want to add a littlecheck test. See below for details.
|
||||
|
||||
Contributing documentation
|
||||
==========================
|
||||
|
||||
The documentation is stored in ``doc_src/``, and written in ReStructured Text and built with Sphinx.
|
||||
|
||||
To build it locally, run from the main fish-shell directory::
|
||||
|
||||
sphinx-build -j 8 -b html -n doc_src/ /tmp/fish-doc/
|
||||
|
||||
which will build the docs as html in /tmp/fish-doc. You can open it in a browser and see that it looks okay.
|
||||
|
||||
The builtins and various functions shipped with fish are documented in doc_src/cmds/.
|
||||
|
||||
Code Style
|
||||
==========
|
||||
|
||||
To ensure your changes conform to the style rules run
|
||||
|
||||
::
|
||||
|
||||
build_tools/style.fish
|
||||
|
||||
before committing your change. That will run our autoformatters:
|
||||
|
||||
- ``rustfmt`` for Rust
|
||||
- ``fish_indent`` (shipped with fish) for fish script
|
||||
- ``black`` for python
|
||||
|
||||
If you’ve already committed your changes that’s okay since it will then
|
||||
check the files in the most recent commit. This can be useful after
|
||||
you’ve merged another person’s change and want to check that it’s style
|
||||
is acceptable. However, in that case it will run ``clang-format`` to
|
||||
ensure the entire file, not just the lines modified by the commit,
|
||||
conform to the style.
|
||||
|
||||
If you want to check the style of the entire code base run
|
||||
|
||||
::
|
||||
|
||||
build_tools/style.fish --all
|
||||
|
||||
That command will refuse to restyle any files if you have uncommitted
|
||||
changes.
|
||||
|
||||
Fish Script Style Guide
|
||||
-----------------------
|
||||
|
||||
1. All fish scripts, such as those in the *share/functions* and *tests*
|
||||
directories, should be formatted using the ``fish_indent`` command.
|
||||
|
||||
2. Function names should be in all lowercase with words separated by
|
||||
underscores. Private functions should begin with an underscore. The
|
||||
first word should be ``fish`` if the function is unique to fish.
|
||||
|
||||
3. The first word of global variable names should generally be ``fish``
|
||||
for public vars or ``_fish`` for private vars to minimize the
|
||||
possibility of name clashes with user defined vars.
|
||||
|
||||
Configuring Your Editor for Fish Scripts
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If you use Vim: Install `vim-fish <https://github.com/dag/vim-fish>`__,
|
||||
make sure you have syntax and filetype functionality in ``~/.vimrc``:
|
||||
|
||||
::
|
||||
|
||||
syntax enable
|
||||
filetype plugin indent on
|
||||
|
||||
Then turn on some options for nicer display of fish scripts in
|
||||
``~/.vim/ftplugin/fish.vim``:
|
||||
|
||||
::
|
||||
|
||||
" Set up :make to use fish for syntax checking.
|
||||
compiler fish
|
||||
|
||||
" Set this to have long lines wrap inside comments.
|
||||
setlocal textwidth=79
|
||||
|
||||
" Enable folding of block structures in fish.
|
||||
setlocal foldmethod=expr
|
||||
|
||||
If you use Emacs: Install
|
||||
`fish-mode <https://github.com/wwwjfy/emacs-fish>`__ (also available in
|
||||
melpa and melpa-stable) and ``(setq-default indent-tabs-mode nil)`` for
|
||||
it (via a hook or in ``use-package``\ s “:init” block). It can also be
|
||||
made to run fish_indent via e.g.
|
||||
|
||||
.. code:: elisp
|
||||
|
||||
(add-hook 'fish-mode-hook (lambda ()
|
||||
(add-hook 'before-save-hook 'fish_indent-before-save)))
|
||||
|
||||
Rust Style Guide
|
||||
----------------
|
||||
|
||||
Use ``cargo fmt`` and ``cargo clippy``. Clippy warnings can be turned off if there's a good reason to.
|
||||
|
||||
Testing
|
||||
=======
|
||||
|
||||
The source code for fish includes a large collection of tests. If you
|
||||
are making any changes to fish, running these tests is a good way to make
|
||||
sure the behaviour remains consistent and regressions are not
|
||||
introduced. Even if you don’t run the tests on your machine, they will
|
||||
still be run via Github Actions.
|
||||
|
||||
You are strongly encouraged to add tests when changing the functionality
|
||||
of fish, especially if you are fixing a bug to help ensure there are no
|
||||
regressions in the future (i.e., we don’t reintroduce the bug).
|
||||
|
||||
The tests can be found in three places:
|
||||
|
||||
- src/tests for unit tests.
|
||||
- tests/checks for script tests, run by `littlecheck <https://github.com/ridiculousfish/littlecheck>`__
|
||||
- tests/pexpects for interactive tests using `pexpect <https://pexpect.readthedocs.io/en/stable/>`__
|
||||
|
||||
When in doubt, the bulk of the tests should be added as a littlecheck test in tests/checks, as they are the easiest to modify and run, and much faster and more dependable than pexpect tests. The syntax is fairly self-explanatory. It's a fish script with the expected output in ``# CHECK:`` or ``# CHECKERR:`` (for stderr) comments.
|
||||
If your littlecheck test has a specific dependency, use ``# REQUIRE: ...`` with a posix sh script.
|
||||
|
||||
The pexpects are written in python and can simulate input and output to/from a terminal, so they are needed for anything that needs actual interactivity. The runner is in tests/pexpect_helper.py, in case you need to modify something there.
|
||||
|
||||
These tests can be run via the tests/test_driver.py python script, which will set up the environment.
|
||||
It sets up a temporary $HOME and also uses it as the current directory, so you do not need to create a temporary directory in them.
|
||||
|
||||
If you need a command to do something weird to test something, maybe add it to the ``fish_test_helper`` binary (in tests/fish_test_helper.c), or see if it can already do it.
|
||||
|
||||
Local testing
|
||||
-------------
|
||||
|
||||
The tests can be run on your local computer on all operating systems.
|
||||
|
||||
::
|
||||
|
||||
cmake path/to/fish-shell
|
||||
make fish_run_tests
|
||||
|
||||
Or you can run them on a fish, without involving cmake::
|
||||
|
||||
cargo build
|
||||
cargo test # for the unit tests
|
||||
tests/test_driver.py target/debug # for the script and interactive tests
|
||||
|
||||
Here, the first argument to test_driver.py refers to a directory with ``fish``, ``fish_indent`` and ``fish_key_reader`` in it.
|
||||
In this example we're in the root of the git repo and have run ``cargo build`` without ``--release``, so it's a debug build.
|
||||
|
||||
Git hooks
|
||||
---------
|
||||
|
||||
Since developers sometimes forget to run the tests, it can be helpful to
|
||||
use git hooks (see githooks(5)) to automate it.
|
||||
|
||||
One possibility is a pre-push hook script like this one:
|
||||
|
||||
.. code:: sh
|
||||
|
||||
#!/bin/sh
|
||||
#### A pre-push hook for the fish-shell project
|
||||
# This will run the tests when a push to master is detected, and will stop that if the tests fail
|
||||
# Save this as .git/hooks/pre-push and make it executable
|
||||
|
||||
protected_branch='master'
|
||||
|
||||
# Git gives us lines like "refs/heads/frombranch SOMESHA1 refs/heads/tobranch SOMESHA1"
|
||||
# We're only interested in the branches
|
||||
isprotected=false
|
||||
while read from _ to _; do
|
||||
if [ "$to" = "refs/heads/$protected_branch" ]; then
|
||||
isprotected=true
|
||||
fi
|
||||
done
|
||||
if "$isprotected"; then
|
||||
echo "Running checks before push to master"
|
||||
build_tools/check.sh
|
||||
fi
|
||||
|
||||
This will check if the push is to the master branch and, if it is, only
|
||||
allow the push if running ``build_tools/check.sh`` succeeds. In some circumstances
|
||||
it may be advisable to circumvent this check with
|
||||
``git push --no-verify``, but usually that isn’t necessary.
|
||||
|
||||
To install the hook, place the code in a new file
|
||||
``.git/hooks/pre-push`` and make it executable.
|
||||
|
||||
Contributing Translations
|
||||
=========================
|
||||
|
||||
Fish uses GNU gettext to translate messages from English to other languages.
|
||||
We use custom tools for extracting messages from source files and to localize at runtime.
|
||||
This means that we do not have a runtime dependency on the gettext library.
|
||||
It also means that some features are not supported, such as message context and plurals.
|
||||
We also expect all files to be UTF-8-encoded.
|
||||
In practice, this should not matter much for contributing translations.
|
||||
|
||||
Translation sources are
|
||||
stored in the ``po`` directory, named ``ll_CC.po``, where ``ll`` is the
|
||||
two (or possibly three) letter ISO 639-1 language code of the target language
|
||||
(e.g. ``pt`` for Portuguese). ``CC`` is an ISO 3166 country/territory code,
|
||||
(e.g. ``BR`` for Brazil).
|
||||
An example for a valid name is ``pt_BR.po``, indicating Brazilian Portuguese.
|
||||
These are the files you will interact with when adding translations.
|
||||
|
||||
Adding translations for a new language
|
||||
--------------------------------------
|
||||
|
||||
Creating new translations requires the Gettext tools.
|
||||
More specifically, you will need ``msguniq`` and ``msgmerge`` for creating translations for a new
|
||||
language.
|
||||
To create a new translation, run::
|
||||
|
||||
build_tools/update_translations.fish po/ll_CC.po
|
||||
|
||||
This will create a new PO file containing all messages available for translation.
|
||||
If the file already exists, it will be updated.
|
||||
|
||||
After modifying a PO file, you can recompile fish, and it will integrate the modifications you made.
|
||||
This requires that the ``msgfmt`` utility is installed (comes as part of ``gettext``).
|
||||
It is important that the ``localize-messages`` cargo feature is enabled, which it is by default.
|
||||
You can explicitly enable it using::
|
||||
|
||||
cargo build --features=localize-messages
|
||||
|
||||
Use environment variables to tell fish which language to use, e.g.::
|
||||
|
||||
LANG=pt_BR.utf8 fish
|
||||
|
||||
or within the running fish shell::
|
||||
|
||||
set LANG pt_BR.utf8
|
||||
|
||||
For more options regarding how to choose languages, see
|
||||
`the corresponding gettext documentation
|
||||
<https://www.gnu.org/software/gettext/manual/html_node/Locale-Environment-Variables.html>`__.
|
||||
One neat thing you can do is set a list of languages to check for translations in the order defined
|
||||
using the ``LANGUAGE`` variable, e.g.::
|
||||
|
||||
set LANGUAGE pt_BR de_DE
|
||||
|
||||
to try to translate messages to Portuguese, if that fails try German, and if that fails too you will
|
||||
see the English version defined in the source code.
|
||||
|
||||
Modifying existing translations
|
||||
-------------------------------
|
||||
|
||||
If you want to work on translations for a language which already has a corresponding ``po`` file, it
|
||||
is sufficient to edit this file. No other changes are necessary.
|
||||
|
||||
After recompiling fish, you should be able to see your translations in action. See the previous
|
||||
section for details.
|
||||
|
||||
Editing PO files
|
||||
----------------
|
||||
|
||||
Many tools are available for editing translation files, including
|
||||
command-line and graphical user interface programs. For simple use, you can use your text editor.
|
||||
|
||||
Open up the PO file, for example ``po/sv.po``, and you'll see something like::
|
||||
|
||||
msgid "%ls: No suitable job\n"
|
||||
msgstr ""
|
||||
|
||||
The ``msgid`` here is the "name" of the string to translate, typically the English string to translate.
|
||||
The second line (``msgstr``) is where your translation goes.
|
||||
|
||||
For example::
|
||||
|
||||
msgid "%ls: No suitable job\n"
|
||||
msgstr "%ls: Inget passande jobb\n"
|
||||
|
||||
Any ``%s`` / ``%ls`` or ``%d`` are placeholders that fish will use for formatting at runtime. It is important that they match - the translated string should have the same placeholders in the same order.
|
||||
|
||||
Also any escaped characters, like that ``\n`` newline at the end, should be kept so the translation has the same behavior.
|
||||
|
||||
Our tests run ``msgfmt --check-format /path/to/file``, so they would catch mismatched placeholders - otherwise fish would crash at runtime when the string is about to be used.
|
||||
|
||||
Be cautious about blindly updating an existing translation file.
|
||||
``msgid`` strings should never be updated manually, only by running the appropriate script.
|
||||
|
||||
Modifications to strings in source files
|
||||
----------------------------------------
|
||||
|
||||
If a string changes in the sources, the old translations will no longer work.
|
||||
They will be preserved in the PO files, but commented-out (starting with ``#~``).
|
||||
If you add/remove/change a translatable strings in a source file,
|
||||
run ``build_tools/update_translations.fish`` to propagate this to all translation files (``po/*.po``).
|
||||
This is only relevant for developers modifying the source files of fish or fish scripts.
|
||||
|
||||
Setting Code Up For Translations
|
||||
--------------------------------
|
||||
|
||||
All non-debug messages output for user consumption should be marked for
|
||||
translation. In Rust, this requires the use of the ``wgettext!`` or ``wgettext_fmt!``
|
||||
macros:
|
||||
|
||||
::
|
||||
|
||||
streams.out.append(wgettext_fmt!("%ls: There are no jobs\n", argv[0]));
|
||||
|
||||
All messages in fish script must be enclosed in single or double quote
|
||||
characters for our message extraction script to find them.
|
||||
They must also be translated via a command substitution. This means
|
||||
that the following are **not** valid:
|
||||
|
||||
::
|
||||
|
||||
echo (_ hello)
|
||||
_ "goodbye"
|
||||
|
||||
Above should be written like this instead:
|
||||
|
||||
::
|
||||
|
||||
echo (_ "hello")
|
||||
echo (_ "goodbye")
|
||||
|
||||
You can use either single or double quotes to enclose the
|
||||
message to be translated. You can also optionally include spaces after
|
||||
the opening parentheses or before the closing parentheses.
|
||||
|
||||
Versioning
|
||||
==========
|
||||
|
||||
The fish version is constructed by the *build_tools/git_version_gen.sh*
|
||||
script. For developers the version is the branch name plus the output of
|
||||
``git describe --always --dirty``. Normally the main part of the version
|
||||
will be the closest annotated tag. Which itself is usually the most
|
||||
recent release number (e.g., ``2.6.0``).
|
||||
20
COPYING
20
COPYING
@@ -1,20 +0,0 @@
|
||||
Fish is a smart and user-friendly command line shell.
|
||||
|
||||
Copyright (C) 2005-2009 Axel Liljencrantz
|
||||
Copyright (C) 2009- fish-shell contributors
|
||||
|
||||
fish is free software.
|
||||
|
||||
Most of fish is licensed under the GNU General Public License version 2, and
|
||||
you can redistribute it and/or modify it under the terms of the GNU GPL as
|
||||
published by the Free Software Foundation.
|
||||
|
||||
fish also includes software licensed under the Python Software Foundation License version 2, the MIT
|
||||
license, and the GNU Library General Public License version 2.
|
||||
|
||||
Full licensing information is contained in doc_src/license.rst.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
more details.
|
||||
768
Cargo.lock
generated
768
Cargo.lock
generated
@@ -1,768 +0,0 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "allocator-api2"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.10.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a012a0df96dd6d06ba9a1b29d6402d1a5d77c6befd2566afdc26e10603dc93d7"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
"libc",
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "cfg_aliases"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"crypto-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||
|
||||
[[package]]
|
||||
name = "fish"
|
||||
version = "4.1.2"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cc",
|
||||
"errno",
|
||||
"fish-build-helper",
|
||||
"fish-build-man-pages",
|
||||
"fish-gettext-extraction",
|
||||
"fish-gettext-maps",
|
||||
"fish-gettext-mo-file-parser",
|
||||
"fish-printf",
|
||||
"libc",
|
||||
"lru",
|
||||
"nix",
|
||||
"num-traits",
|
||||
"once_cell",
|
||||
"pcre2",
|
||||
"phf 0.12.1",
|
||||
"phf_codegen 0.12.1",
|
||||
"portable-atomic",
|
||||
"rand",
|
||||
"rsconf",
|
||||
"rust-embed",
|
||||
"serial_test",
|
||||
"terminfo",
|
||||
"unix_path",
|
||||
"widestring",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fish-build-helper"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"rsconf",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fish-build-man-pages"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"fish-build-helper",
|
||||
"rsconf",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fish-gettext-extraction"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fish-gettext-maps"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"fish-build-helper",
|
||||
"fish-gettext-mo-file-parser",
|
||||
"phf 0.12.1",
|
||||
"phf_codegen 0.12.1",
|
||||
"rsconf",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fish-gettext-mo-file-parser"
|
||||
version = "0.0.0"
|
||||
|
||||
[[package]]
|
||||
name = "fish-printf"
|
||||
version = "0.2.1"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"unicode-segmentation",
|
||||
"unicode-width",
|
||||
"widestring",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "foldhash"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f"
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
|
||||
dependencies = [
|
||||
"allocator-api2",
|
||||
"equivalent",
|
||||
"foldhash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jobserver"
|
||||
version = "0.1.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.172"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
||||
|
||||
[[package]]
|
||||
name = "lru"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "227748d55f2f0ab4735d87fd623798cb6b664512fe979705f829c9f81c934465"
|
||||
dependencies = [
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
|
||||
[[package]]
|
||||
name = "minimal-lexical"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.30.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"cfg_aliases",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "7.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"minimal-lexical",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pcre2"
|
||||
version = "0.2.9"
|
||||
source = "git+https://github.com/fish-shell/rust-pcre2?tag=0.2.9-utf32#85b7afba1a9d9bd445779800e5bcafeb732e4421"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"pcre2-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pcre2-sys"
|
||||
version = "0.2.9"
|
||||
source = "git+https://github.com/fish-shell/rust-pcre2?tag=0.2.9-utf32#85b7afba1a9d9bd445779800e5bcafeb732e4421"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078"
|
||||
dependencies = [
|
||||
"phf_shared 0.11.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "913273894cec178f401a31ec4b656318d95473527be05c0752cc41cdc32be8b7"
|
||||
dependencies = [
|
||||
"phf_shared 0.12.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_codegen"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a"
|
||||
dependencies = [
|
||||
"phf_generator 0.11.3",
|
||||
"phf_shared 0.11.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_codegen"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "efbdcb6f01d193b17f0b9c3360fa7e0e620991b193ff08702f78b3ce365d7e61"
|
||||
dependencies = [
|
||||
"phf_generator 0.12.1",
|
||||
"phf_shared 0.12.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_generator"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d"
|
||||
dependencies = [
|
||||
"phf_shared 0.11.3",
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_generator"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2cbb1126afed61dd6368748dae63b1ee7dc480191c6262a3b4ff1e29d86a6c5b"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"phf_shared 0.12.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_shared"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5"
|
||||
dependencies = [
|
||||
"siphasher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_shared"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06005508882fb681fd97892ecff4b7fd0fee13ef1aa569f8695dae7ab9099981"
|
||||
dependencies = [
|
||||
"siphasher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2"
|
||||
|
||||
[[package]]
|
||||
name = "portable-atomic"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.95"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rsconf"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd2af859f1af0401e7fc7577739c87b0d239d8a5da400d717183bca92336bcdc"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-embed"
|
||||
version = "8.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "025908b8682a26ba8d12f6f2d66b987584a4a87bc024abc5bbc12553a8cd178a"
|
||||
dependencies = [
|
||||
"rust-embed-impl",
|
||||
"rust-embed-utils",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-embed-impl"
|
||||
version = "8.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6065f1a4392b71819ec1ea1df1120673418bf386f50de1d6f54204d836d4349c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rust-embed-utils",
|
||||
"syn",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-embed-utils"
|
||||
version = "8.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6cc0c81648b20b70c491ff8cce00c1c3b223bb8ed2b5d41f0e54c6c4c0a3594"
|
||||
dependencies = [
|
||||
"sha2",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scc"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28e1c91382686d21b5ac7959341fcb9780fa7c03773646995a87c950fa7be640"
|
||||
dependencies = [
|
||||
"sdd",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "sdd"
|
||||
version = "3.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "478f121bb72bbf63c52c93011ea1791dca40140dfe13f8336c4c5ac952c33aa9"
|
||||
|
||||
[[package]]
|
||||
name = "serial_test"
|
||||
version = "3.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b258109f244e1d6891bf1053a55d63a5cd4f8f4c30cf9a1280989f80e7a1fa9"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"parking_lot",
|
||||
"scc",
|
||||
"serial_test_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serial_test_derive"
|
||||
version = "3.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d69265a08751de7844521fd15003ae0a888e035773ba05695c5c759a6f89eef"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.10.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "siphasher"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.95"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46f71c0377baf4ef1cc3e3402ded576dccc315800fbc62dfc7fe04b009773b4a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "terminfo"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4ea810f0692f9f51b382fff5893887bb4580f5fa246fde546e0b13e7fcee662"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"nom",
|
||||
"phf 0.11.3",
|
||||
"phf_codegen 0.11.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
|
||||
|
||||
[[package]]
|
||||
name = "unix_path"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af8e291873ae77c4c8d9c9b34d0bee68a35b048fb39c263a5155e0e353783eaf"
|
||||
dependencies = [
|
||||
"unix_str",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unix_str"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ace0b4755d0a2959962769239d56267f8a024fef2d9b32666b3dcd0946b0906"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
|
||||
dependencies = [
|
||||
"same-file",
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "widestring"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd7cf3379ca1aac9eea11fba24fd7e315d621f8dfe35c8d7d2be8b793726e07d"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
166
Cargo.toml
166
Cargo.toml
@@ -1,166 +0,0 @@
|
||||
[workspace]
|
||||
resolver = "2"
|
||||
members = ["crates/*"]
|
||||
|
||||
[workspace.package]
|
||||
# To build revisions that use Corrosion (those before 2024-01), use CMake 3.19, Rustc 1.78 and Rustup 1.27.
|
||||
rust-version = "1.70"
|
||||
edition = "2021"
|
||||
repository = "https://github.com/fish-shell/fish-shell"
|
||||
|
||||
[workspace.dependencies]
|
||||
bitflags = "2.5.0"
|
||||
cc = "1.0.94"
|
||||
errno = "0.3.0"
|
||||
fish-build-helper = { path = "crates/build-helper" }
|
||||
fish-build-man-pages = { path = "crates/build-man-pages" }
|
||||
fish-gettext-extraction = { path = "crates/gettext-extraction" }
|
||||
fish-gettext-maps = { path = "crates/gettext-maps" }
|
||||
fish-gettext-mo-file-parser = { path = "crates/gettext-mo-file-parser" }
|
||||
fish-printf = { path = "crates/printf", features = ["widestring"] }
|
||||
libc = "0.2.155"
|
||||
# lru pulls in hashbrown by default, which uses a faster (though less DoS resistant) hashing algo.
|
||||
# disabling default features uses the stdlib instead, but it doubles the time to rewrite the history
|
||||
# files as of 22 April 2024.
|
||||
lru = "0.13.0"
|
||||
nix = { version = "0.30.1", default-features = false, features = [
|
||||
"event",
|
||||
"inotify",
|
||||
"resource",
|
||||
"fs",
|
||||
] }
|
||||
num-traits = "0.2.19"
|
||||
once_cell = "1.19.0"
|
||||
pcre2 = { git = "https://github.com/fish-shell/rust-pcre2", tag = "0.2.9-utf32", default-features = false, features = [
|
||||
"utf32",
|
||||
] }
|
||||
phf = { version = "0.12", default-features = false }
|
||||
phf_codegen = { version = "0.12" }
|
||||
portable-atomic = { version = "1", default-features = false, features = [
|
||||
"fallback",
|
||||
] }
|
||||
proc-macro2 = "1.0"
|
||||
# Don't use the "getrandom" feature as it requires "getentropy" which was not
|
||||
# available on macOS < 10.12. We can enable "getrandom" when we raise the
|
||||
# minimum supported version to 10.12.
|
||||
rand = { version = "0.8.5", default-features = false, features = ["small_rng"] }
|
||||
rsconf = "0.2.2"
|
||||
rust-embed = { version = "8.7.2", features = ["deterministic-timestamps"] }
|
||||
serial_test = { version = "3", default-features = false }
|
||||
# We need 0.9.0 specifically for some crash fixes.
|
||||
terminfo = "0.9.0"
|
||||
widestring = "1.2.0"
|
||||
unicode-segmentation = "1.12.0"
|
||||
unicode-width = "0.2.0"
|
||||
unix_path = "1.0.1"
|
||||
|
||||
[profile.release]
|
||||
overflow-checks = true
|
||||
lto = true
|
||||
|
||||
[profile.release-with-debug]
|
||||
inherits = "release"
|
||||
debug = true
|
||||
|
||||
[package]
|
||||
name = "fish"
|
||||
version = "4.1.2"
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
default-run = "fish"
|
||||
# see doc_src/license.rst for details
|
||||
# don't forget to update COPYING and debian/copyright too
|
||||
license = "GPL-2.0-only AND LGPL-2.0-or-later AND MIT AND PSF-2.0"
|
||||
homepage = "https://fishshell.com"
|
||||
readme = "README.rst"
|
||||
|
||||
[dependencies]
|
||||
bitflags.workspace = true
|
||||
errno.workspace = true
|
||||
fish-build-helper.workspace = true
|
||||
fish-build-man-pages = { workspace = true, optional = true }
|
||||
fish-gettext-extraction = { workspace = true, optional = true }
|
||||
fish-gettext-maps = { workspace = true, optional = true }
|
||||
fish-printf.workspace = true
|
||||
libc.workspace = true
|
||||
lru.workspace = true
|
||||
nix.workspace = true
|
||||
num-traits.workspace = true
|
||||
once_cell.workspace = true
|
||||
pcre2.workspace = true
|
||||
phf = { workspace = true, optional = true }
|
||||
rand.workspace = true
|
||||
terminfo.workspace = true
|
||||
widestring.workspace = true
|
||||
|
||||
[target.'cfg(not(target_has_atomic = "64"))'.dependencies]
|
||||
portable-atomic.workspace = true
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
rust-embed = { workspace = true, optional = true, features = ["deterministic-timestamps", "debug-embed"] }
|
||||
[target.'cfg(not(windows))'.dependencies]
|
||||
rust-embed = { workspace = true, optional = true, features = ["deterministic-timestamps"] }
|
||||
|
||||
[dev-dependencies]
|
||||
serial_test.workspace = true
|
||||
|
||||
[build-dependencies]
|
||||
cc.workspace = true
|
||||
fish-build-helper.workspace = true
|
||||
fish-gettext-mo-file-parser.workspace = true
|
||||
phf_codegen = { workspace = true, optional = true }
|
||||
rsconf.workspace = true
|
||||
|
||||
[target.'cfg(windows)'.build-dependencies]
|
||||
unix_path.workspace = true
|
||||
|
||||
[lib]
|
||||
crate-type = ["rlib"]
|
||||
path = "src/lib.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "fish"
|
||||
path = "src/bin/fish.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "fish_indent"
|
||||
path = "src/bin/fish_indent.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "fish_key_reader"
|
||||
path = "src/bin/fish_key_reader.rs"
|
||||
|
||||
[features]
|
||||
default = ["embed-data", "localize-messages"]
|
||||
benchmark = []
|
||||
embed-data = ["dep:rust-embed", "dep:fish-build-man-pages"]
|
||||
# Enable gettext localization at runtime. Requires the `msgfmt` tool to generate catalog data at
|
||||
# build time.
|
||||
localize-messages = ["dep:phf", "dep:fish-gettext-maps"]
|
||||
# This feature is used to enable extracting messages from the source code for localization.
|
||||
# It only needs to be enabled if updating these messages (and the corresponding PO files) is
|
||||
# desired. This happens when running tests via `build_tools/check.sh` and when calling
|
||||
# `build_tools/update_translations.fish`, so there should not be a need to enable it manually.
|
||||
gettext-extract = ["dep:fish-gettext-extraction"]
|
||||
|
||||
# The following features are auto-detected by the build-script and should not be enabled manually.
|
||||
asan = []
|
||||
tsan = []
|
||||
|
||||
[workspace.lints]
|
||||
rust.non_camel_case_types = "allow"
|
||||
rust.non_upper_case_globals = "allow"
|
||||
rust.unknown_lints = "allow"
|
||||
rust.unstable_name_collisions = "allow"
|
||||
clippy.manual_range_contains = "allow"
|
||||
clippy.needless_return = "allow"
|
||||
clippy.needless_lifetimes = "allow"
|
||||
|
||||
# We do not want to use the e?print(ln)?! macros.
|
||||
# These lints flag their use.
|
||||
# In the future, they might change to flag other methods of printing.
|
||||
clippy.print_stdout = "deny"
|
||||
clippy.print_stderr = "deny"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
18
Dockerfile
18
Dockerfile
@@ -1,18 +0,0 @@
|
||||
FROM centos:latest
|
||||
|
||||
# Build dependency
|
||||
RUN yum update -y &&\
|
||||
yum install -y epel-release &&\
|
||||
yum install -y clang cmake3 gcc-c++ make &&\
|
||||
yum clean all
|
||||
|
||||
# Test dependency
|
||||
RUN yum install -y expect vim-common
|
||||
|
||||
ADD . /src
|
||||
WORKDIR /src
|
||||
|
||||
# Build fish
|
||||
RUN cmake3 . &&\
|
||||
make &&\
|
||||
make install
|
||||
1161
Doxyfile.help
Normal file
1161
Doxyfile.help
Normal file
File diff suppressed because it is too large
Load Diff
161
Doxyfile.user
Normal file
161
Doxyfile.user
Normal file
@@ -0,0 +1,161 @@
|
||||
PROJECT_NAME = fish
|
||||
PROJECT_NUMBER = 1
|
||||
OUTPUT_DIRECTORY = user_doc
|
||||
CREATE_SUBDIRS = NO
|
||||
OUTPUT_LANGUAGE = English
|
||||
USE_WINDOWS_ENCODING = NO
|
||||
BRIEF_MEMBER_DESC = YES
|
||||
REPEAT_BRIEF = YES
|
||||
ABBREVIATE_BRIEF = YES
|
||||
ALWAYS_DETAILED_SEC = NO
|
||||
INLINE_INHERITED_MEMB = NO
|
||||
FULL_PATH_NAMES = YES
|
||||
STRIP_FROM_PATH =
|
||||
STRIP_FROM_INC_PATH =
|
||||
SHORT_NAMES = NO
|
||||
JAVADOC_AUTOBRIEF = YES
|
||||
MULTILINE_CPP_IS_BRIEF = NO
|
||||
DETAILS_AT_TOP = NO
|
||||
INHERIT_DOCS = YES
|
||||
DISTRIBUTE_GROUP_DOC = NO
|
||||
TAB_SIZE = 8
|
||||
ALIASES =
|
||||
OPTIMIZE_OUTPUT_FOR_C = YES
|
||||
OPTIMIZE_OUTPUT_JAVA = NO
|
||||
SUBGROUPING = YES
|
||||
EXTRACT_ALL = NO
|
||||
EXTRACT_PRIVATE = NO
|
||||
EXTRACT_STATIC = YES
|
||||
EXTRACT_LOCAL_CLASSES = YES
|
||||
EXTRACT_LOCAL_METHODS = NO
|
||||
HIDE_UNDOC_MEMBERS = NO
|
||||
HIDE_UNDOC_CLASSES = NO
|
||||
HIDE_FRIEND_COMPOUNDS = NO
|
||||
HIDE_IN_BODY_DOCS = NO
|
||||
INTERNAL_DOCS = NO
|
||||
CASE_SENSE_NAMES = YES
|
||||
HIDE_SCOPE_NAMES = NO
|
||||
SHOW_INCLUDE_FILES = YES
|
||||
INLINE_INFO = YES
|
||||
SORT_MEMBER_DOCS = YES
|
||||
SORT_BRIEF_DOCS = NO
|
||||
SORT_BY_SCOPE_NAME = NO
|
||||
GENERATE_TODOLIST = YES
|
||||
GENERATE_TESTLIST = YES
|
||||
GENERATE_BUGLIST = YES
|
||||
GENERATE_DEPRECATEDLIST= YES
|
||||
ENABLED_SECTIONS =
|
||||
MAX_INITIALIZER_LINES = 30
|
||||
SHOW_USED_FILES = YES
|
||||
SHOW_DIRECTORIES = YES
|
||||
QUIET = NO
|
||||
WARNINGS = YES
|
||||
WARN_IF_UNDOCUMENTED = YES
|
||||
WARN_IF_DOC_ERROR = YES
|
||||
WARN_FORMAT = "$file:$line: $text"
|
||||
WARN_LOGFILE =
|
||||
INPUT =
|
||||
FILE_PATTERNS = doc.h
|
||||
RECURSIVE = NO
|
||||
EXCLUDE =
|
||||
EXCLUDE_SYMLINKS = NO
|
||||
EXCLUDE_PATTERNS =
|
||||
EXAMPLE_PATH =
|
||||
EXAMPLE_PATTERNS =
|
||||
EXAMPLE_RECURSIVE = NO
|
||||
IMAGE_PATH =
|
||||
INPUT_FILTER =
|
||||
FILTER_PATTERNS =
|
||||
FILTER_SOURCE_FILES = NO
|
||||
SOURCE_BROWSER = NO
|
||||
INLINE_SOURCES = NO
|
||||
STRIP_CODE_COMMENTS = YES
|
||||
REFERENCED_BY_RELATION = YES
|
||||
REFERENCES_RELATION = YES
|
||||
VERBATIM_HEADERS = YES
|
||||
ALPHABETICAL_INDEX = NO
|
||||
COLS_IN_ALPHA_INDEX = 5
|
||||
IGNORE_PREFIX =
|
||||
GENERATE_HTML = YES
|
||||
HTML_OUTPUT = html
|
||||
HTML_FILE_EXTENSION = .html
|
||||
HTML_HEADER = user_doc.head.html
|
||||
HTML_FOOTER =
|
||||
HTML_STYLESHEET =
|
||||
HTML_ALIGN_MEMBERS = YES
|
||||
GENERATE_HTMLHELP = NO
|
||||
CHM_FILE =
|
||||
HHC_LOCATION =
|
||||
GENERATE_CHI = NO
|
||||
BINARY_TOC = NO
|
||||
TOC_EXPAND = NO
|
||||
DISABLE_INDEX = YES
|
||||
ENUM_VALUES_PER_LINE = 4
|
||||
GENERATE_TREEVIEW = NO
|
||||
TREEVIEW_WIDTH = 250
|
||||
GENERATE_LATEX = NO
|
||||
LATEX_OUTPUT = latex
|
||||
LATEX_CMD_NAME = latex
|
||||
MAKEINDEX_CMD_NAME = makeindex
|
||||
COMPACT_LATEX = NO
|
||||
PAPER_TYPE = a4wide
|
||||
EXTRA_PACKAGES =
|
||||
LATEX_HEADER =
|
||||
PDF_HYPERLINKS = YES
|
||||
USE_PDFLATEX = YES
|
||||
LATEX_BATCHMODE = NO
|
||||
LATEX_HIDE_INDICES = NO
|
||||
GENERATE_RTF = NO
|
||||
RTF_OUTPUT = rtf
|
||||
COMPACT_RTF = NO
|
||||
RTF_HYPERLINKS = NO
|
||||
RTF_STYLESHEET_FILE =
|
||||
RTF_EXTENSIONS_FILE =
|
||||
GENERATE_MAN = NO
|
||||
MAN_OUTPUT = man
|
||||
MAN_EXTENSION = .3
|
||||
MAN_LINKS = NO
|
||||
GENERATE_XML = NO
|
||||
XML_OUTPUT = xml
|
||||
XML_SCHEMA =
|
||||
XML_DTD =
|
||||
XML_PROGRAMLISTING = YES
|
||||
GENERATE_AUTOGEN_DEF = NO
|
||||
GENERATE_PERLMOD = NO
|
||||
PERLMOD_LATEX = NO
|
||||
PERLMOD_PRETTY = YES
|
||||
PERLMOD_MAKEVAR_PREFIX =
|
||||
ENABLE_PREPROCESSING = YES
|
||||
MACRO_EXPANSION = NO
|
||||
EXPAND_ONLY_PREDEF = NO
|
||||
SEARCH_INCLUDES = YES
|
||||
INCLUDE_PATH =
|
||||
INCLUDE_FILE_PATTERNS =
|
||||
PREDEFINED =
|
||||
EXPAND_AS_DEFINED =
|
||||
SKIP_FUNCTION_MACROS = YES
|
||||
TAGFILES =
|
||||
GENERATE_TAGFILE =
|
||||
ALLEXTERNALS = NO
|
||||
EXTERNAL_GROUPS = YES
|
||||
PERL_PATH = /usr/bin/perl
|
||||
CLASS_DIAGRAMS = YES
|
||||
HIDE_UNDOC_RELATIONS = YES
|
||||
HAVE_DOT = NO
|
||||
CLASS_GRAPH = NO
|
||||
COLLABORATION_GRAPH = YES
|
||||
UML_LOOK = NO
|
||||
TEMPLATE_RELATIONS = NO
|
||||
INCLUDE_GRAPH = NO
|
||||
INCLUDED_BY_GRAPH = YES
|
||||
CALL_GRAPH = YES
|
||||
GRAPHICAL_HIERARCHY = YES
|
||||
DOT_IMAGE_FORMAT = png
|
||||
DOT_PATH =
|
||||
DOTFILE_DIRS =
|
||||
MAX_DOT_GRAPH_WIDTH = 750
|
||||
MAX_DOT_GRAPH_HEIGHT = 1024
|
||||
MAX_DOT_GRAPH_DEPTH = 0
|
||||
GENERATE_LEGEND = YES
|
||||
DOT_CLEANUP = YES
|
||||
SEARCHENGINE = NO
|
||||
76
GNUmakefile
76
GNUmakefile
@@ -1,76 +0,0 @@
|
||||
# This is a very basic `make` wrapper around the CMake build toolchain.
|
||||
#
|
||||
# Supported arguments:
|
||||
# PREFIX: sets the installation prefix
|
||||
# GENERATOR: explicitly specifies the CMake generator to use
|
||||
|
||||
CMAKE ?= cmake
|
||||
|
||||
GENERATOR ?= $(shell (which ninja > /dev/null 2> /dev/null && echo Ninja) || \
|
||||
echo 'Unix Makefiles')
|
||||
prefix ?= /usr/local
|
||||
PREFIX ?= $(prefix)
|
||||
|
||||
ifeq ($(GENERATOR), Ninja)
|
||||
BUILDFILE = build.ninja
|
||||
else
|
||||
BUILDFILE = Makefile
|
||||
endif
|
||||
|
||||
|
||||
# If CMake has generated an in-tree Makefile, use that instead (issue #6264)
|
||||
MAKE_DIR:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
|
||||
ifeq ($(shell test -f $(MAKE_DIR)/Makefile && echo 1), 1)
|
||||
|
||||
all:
|
||||
@+$(MAKE) -f $(MAKE_DIR)/Makefile $(MAKECMDGOALS) --no-print-directory
|
||||
%:
|
||||
@+$(MAKE) -f $(MAKE_DIR)/Makefile $(MAKECMDGOALS) --no-print-directory
|
||||
|
||||
else
|
||||
|
||||
all: .begin build/fish
|
||||
|
||||
.PHONY: .begin
|
||||
.begin:
|
||||
@which $(CMAKE) > /dev/null 2> /dev/null || \
|
||||
(echo 'Please install CMake and then re-run the `make` command!' 1>&2 && false)
|
||||
|
||||
.PHONY: build/fish
|
||||
build/fish: build/$(BUILDFILE)
|
||||
$(CMAKE) --build build
|
||||
|
||||
# Use build as an order-only dependency. This prevents the target from always being outdated
|
||||
# after a make run, and more importantly, doesn't clobber manually specified CMake options.
|
||||
build/$(BUILDFILE): | build
|
||||
cd build; $(CMAKE) .. -G "$(GENERATOR)" \
|
||||
-DCMAKE_INSTALL_PREFIX="$(PREFIX)" -DCMAKE_EXPORT_COMPILE_COMMANDS=1
|
||||
|
||||
build:
|
||||
mkdir -p build
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -rf build
|
||||
|
||||
.PHONY: test
|
||||
test: build/fish
|
||||
$(CMAKE) --build build --target fish_run_tests
|
||||
|
||||
.PHONY: fish_run_tests
|
||||
fish_run_tests: build/fish
|
||||
$(CMAKE) --build build --target fish_run_tests
|
||||
|
||||
.PHONY: install
|
||||
install: build/fish
|
||||
$(CMAKE) --build build --target install
|
||||
|
||||
.PHONY: run
|
||||
run: build/fish
|
||||
./build/fish || true
|
||||
|
||||
.PHONY: exec
|
||||
exec: build/fish
|
||||
exec ./build/fish
|
||||
|
||||
endif # CMake in-tree build check
|
||||
985
Makefile.in
Normal file
985
Makefile.in
Normal file
@@ -0,0 +1,985 @@
|
||||
|
||||
# Copyright (C) 2005-2006 Axel Liljencrantz
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License version 2 as
|
||||
# published by the Free Software Foundation.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
#
|
||||
|
||||
#
|
||||
# @configure_input@
|
||||
#
|
||||
|
||||
#
|
||||
# Makefile for the fish shell. Can build fish and associated
|
||||
# applications, install them, recalculate dependencies and also create
|
||||
# binary distributions in tar.bz2, tar.gz and rpm formats.
|
||||
#
|
||||
|
||||
#
|
||||
# The fish buildprocess is quite complex. Do not stare directly into
|
||||
# the Makefile. Doing so may cause nausea, dizziness and
|
||||
# hallucinations.
|
||||
#
|
||||
|
||||
# Used by docdir
|
||||
PACKAGE_TARNAME = @PACKAGE_TARNAME@
|
||||
|
||||
#
|
||||
# Programs
|
||||
#
|
||||
|
||||
CXX := @CXX@
|
||||
INSTALL:=@INSTALL@
|
||||
|
||||
|
||||
#
|
||||
# Installation directories
|
||||
#
|
||||
|
||||
prefix = @prefix@
|
||||
exec_prefix = @exec_prefix@
|
||||
datarootdir = @datarootdir@
|
||||
datadir = @datadir@
|
||||
bindir = @bindir@
|
||||
mandir = @mandir@
|
||||
sysconfdir = @sysconfdir@
|
||||
docdir = @docdir@
|
||||
localedir = @localedir@
|
||||
optbindirs = @optbindirs@
|
||||
|
||||
#
|
||||
# Various flags
|
||||
#
|
||||
|
||||
MACROS = -DLOCALEDIR=\"$(localedir)\" -DPREFIX=L\"$(prefix)\" -DDATADIR=L\"$(datadir)\" -DSYSCONFDIR=L\"$(sysconfdir)\" -DBINDIR=L\"$(bindir)\"
|
||||
CXXFLAGS = @CXXFLAGS@ $(MACROS) $(EXTRA_CXXFLAGS)
|
||||
LDFLAGS = @LIBS@ @LDFLAGS@
|
||||
LDFLAGS_FISH = ${LDFLAGS} @LIBS_FISH@ @LDFLAGS_FISH@
|
||||
LDFLAGS_FISH_INDENT = ${LDFLAGS} @LIBS_FISH_INDENT@
|
||||
LDFLAGS_FISH_PAGER = ${LDFLAGS} @LIBS_FISH_PAGER@
|
||||
LDFLAGS_FISHD = ${LDFLAGS} @LIBS_FISHD@
|
||||
LDFLAGS_MIMEDB = ${LDFLAGS} @LIBS_MIMEDB@
|
||||
|
||||
#
|
||||
# Set to 1 if we have gettext
|
||||
#
|
||||
|
||||
HAVE_GETTEXT=@HAVE_GETTEXT@
|
||||
|
||||
|
||||
#
|
||||
#Additional .cpp files used by common.o. These also have a corresponding
|
||||
#.h file.
|
||||
#
|
||||
|
||||
COMMON_FILES := util.cpp fallback.cpp
|
||||
|
||||
|
||||
#
|
||||
# All objects that the system needs to build fish, except fish.o
|
||||
#
|
||||
|
||||
FISH_OBJS := function.o builtin.o complete.o env.o exec.o expand.o \
|
||||
highlight.o history.o kill.o parser.o proc.o reader.o sanity.o \
|
||||
tokenizer.o wildcard.o wgetopt.o wutil.o input.o output.o intern.o \
|
||||
env_universal.o env_universal_common.o input_common.o event.o \
|
||||
signal.o io.o parse_util.o common.o screen.o path.o autoload.o \
|
||||
parser_keywords.o iothread.o color.o postfork.o \
|
||||
builtin_test.o
|
||||
|
||||
FISH_INDENT_OBJS := fish_indent.o print_help.o common.o \
|
||||
parser_keywords.o wutil.o tokenizer.o
|
||||
|
||||
#
|
||||
# Additional files used by builtin.o
|
||||
#
|
||||
|
||||
BUILTIN_FILES := builtin_set.cpp builtin_commandline.cpp \
|
||||
builtin_ulimit.cpp builtin_complete.cpp builtin_jobs.cpp \
|
||||
builtin_set_color.cpp builtin_printf.cpp
|
||||
|
||||
|
||||
#
|
||||
# All objects that the system needs to build fish_pager
|
||||
#
|
||||
|
||||
FISH_PAGER_OBJS := fish_pager.o output.o wutil.o \
|
||||
input_common.o env_universal.o env_universal_common.o common.o \
|
||||
print_help.o iothread.o color.o
|
||||
|
||||
|
||||
#
|
||||
# All objects that the system needs to build fish_tests
|
||||
#
|
||||
|
||||
FISH_TESTS_OBJS := $(FISH_OBJS) fish_tests.o
|
||||
|
||||
|
||||
#
|
||||
# All objects that the system needs to build fishd
|
||||
#
|
||||
|
||||
FISHD_OBJS := fishd.o env_universal_common.o wutil.o print_help.o \
|
||||
common.o
|
||||
|
||||
|
||||
#
|
||||
# All objects needed to build mimedb
|
||||
#
|
||||
|
||||
MIME_OBJS := mimedb.o print_help.o xdgmimealias.o xdgmime.o \
|
||||
xdgmimeglob.o xdgmimeint.o xdgmimemagic.o xdgmimeparent.o wutil.o \
|
||||
common.o
|
||||
|
||||
|
||||
#
|
||||
# Files containing user documentation
|
||||
#
|
||||
|
||||
#
|
||||
# These files are the source files, they contain a few @FOO@-style substitutions
|
||||
#
|
||||
|
||||
HDR_FILES_SRC := doc_src/index.hdr.in doc_src/commands.hdr.in doc_src/design.hdr doc_src/license.hdr doc_src/faq.hdr
|
||||
|
||||
|
||||
#
|
||||
# These are the generated result files
|
||||
#
|
||||
|
||||
HDR_FILES := $(HDR_FILES_SRC:.hdr.in=.hdr)
|
||||
|
||||
# Use a pattern rule so that Make knows to only issue one invocation
|
||||
# per http://www.gnu.org/software/make/manual/make.html#Pattern-Intro
|
||||
|
||||
# Internalized scripts are currently disabled.
|
||||
# For now, we just generate empty arrays.
|
||||
# To generate them again, you would run this:
|
||||
# ./internalize_scripts.py share/functions/*.fish share/completions/*.fish
|
||||
|
||||
|
||||
#
|
||||
# Files containing documentation for external commands.
|
||||
#
|
||||
|
||||
HELP_SRC := $(wildcard doc_src/*.txt)
|
||||
|
||||
|
||||
#
|
||||
# Files in the test directory
|
||||
#
|
||||
|
||||
TEST_IN := $(wildcard tests/test*.in)
|
||||
|
||||
|
||||
#
|
||||
# Files that should be added to the tar archives
|
||||
#
|
||||
|
||||
#
|
||||
# Files in ./doc_src/
|
||||
#
|
||||
|
||||
DOC_SRC_DIR_FILES := $(HDR_FILES_SRC) $(HELP_SRC)
|
||||
|
||||
|
||||
#
|
||||
# Files in ./
|
||||
#
|
||||
|
||||
MAIN_DIR_FILES_UNSORTED := Doxyfile Doxyfile.user Doxyfile.help \
|
||||
Makefile.in configure configure.ac config.h.in install-sh \
|
||||
key_reader.cpp $(MIME_OBJS:.o=.h) \
|
||||
$(MIME_OBJS:.o=.cpp) $(FISH_OBJS:.o=.h) $(BUILTIN_FILES) \
|
||||
$(COMMON_FILES) $(COMMON_FILES:.cpp=.h) $(FISH_OBJS:.o=.cpp) \
|
||||
fish.spec.in INSTALL README user_doc.head.html \
|
||||
ChangeLog config.sub config.guess fish_tests.cpp fish.cpp fish_pager.cpp \
|
||||
fishd.cpp make_vcs_completions.fish $(FISH_INDENT_OBJS:.o=.cpp)
|
||||
|
||||
#
|
||||
# The sorting is not meaningful in itself, but it has the side effect
|
||||
# of removing duplicates, which means there will be fewer warnings
|
||||
# during building.
|
||||
#
|
||||
|
||||
MAIN_DIR_FILES := $(sort $(MAIN_DIR_FILES_UNSORTED))
|
||||
|
||||
|
||||
#
|
||||
# Files in ./tests/
|
||||
#
|
||||
|
||||
TESTS_DIR_FILES := $(TEST_IN) $(TEST_IN:.in=.out) $(TEST_IN:.in=.err) \
|
||||
$(TEST_IN:.in=.status) tests/test.fish tests/gen_output.fish
|
||||
|
||||
|
||||
#
|
||||
# Files in ./share/completions/
|
||||
#
|
||||
|
||||
COMPLETIONS_DIR_FILES := $(wildcard share/completions/*.fish)
|
||||
|
||||
|
||||
#
|
||||
# Files in ./share/functions/
|
||||
#
|
||||
|
||||
FUNCTIONS_DIR_FILES := $(wildcard share/functions/*.fish)
|
||||
|
||||
|
||||
#
|
||||
# Programs to install
|
||||
#
|
||||
|
||||
PROGRAMS := fish mimedb fish_pager fishd fish_indent
|
||||
|
||||
#
|
||||
# Manual pages to install
|
||||
#
|
||||
|
||||
MANUALS := $(addsuffix .1, $(addprefix share/man/man1/, \
|
||||
$(PROGRAMS)))
|
||||
|
||||
|
||||
#
|
||||
# All translation message catalogs
|
||||
#
|
||||
|
||||
TRANSLATIONS_SRC := $(wildcard po/*.po)
|
||||
TRANSLATIONS := $(TRANSLATIONS_SRC:.po=.gmo)
|
||||
|
||||
|
||||
#
|
||||
# Make everything needed for installing fish
|
||||
#
|
||||
|
||||
all: $(PROGRAMS) user_doc share/man $(TRANSLATIONS)
|
||||
@echo fish has now been built.
|
||||
@echo Use \'$(MAKE) install\' to install fish.
|
||||
.PHONY: all
|
||||
|
||||
|
||||
#
|
||||
# These dependencies make sure that autoconf and configure are run
|
||||
# when the source code for the build configuration has changed.
|
||||
#
|
||||
|
||||
configure: configure.ac
|
||||
./config.status --recheck
|
||||
|
||||
Makefile: Makefile.in configure
|
||||
./config.status
|
||||
|
||||
|
||||
#
|
||||
# Build fish with some debug flags specified. This is GCC specific,
|
||||
# and should only be used when debuging fish.
|
||||
#
|
||||
|
||||
prof: EXTRA_CXXFLAGS += -pg
|
||||
prof: LDFLAGS += -pg
|
||||
prof: all
|
||||
.PHONY: prof
|
||||
|
||||
#
|
||||
# User documentation, describing the features of the fish shell.
|
||||
#
|
||||
|
||||
# Depend on the sources (*.hdr.in) and manually make the
|
||||
# intermediate *.hdr and doc.h files if needed
|
||||
# Allow doxygen to fail, e.g. if it does not exist
|
||||
|
||||
user_doc: $(HDR_FILES_SRC) Doxyfile.user user_doc.head.html $(HELP_SRC) doc.h $(HDR_FILES)
|
||||
- (cat Doxyfile.user ; echo PROJECT_NUMBER=@PACKAGE_VERSION@) | doxygen - && touch user_doc
|
||||
|
||||
|
||||
#
|
||||
# Source code documentation. Also includes user documentation.
|
||||
#
|
||||
|
||||
doc: *.h *.cpp doc.h Doxyfile
|
||||
(cat Doxyfile ; echo PROJECT_NUMBER=@PACKAGE_VERSION@) | doxygen - ;
|
||||
|
||||
|
||||
#
|
||||
# PDF version of the source code documentation.
|
||||
#
|
||||
|
||||
doc/refman.pdf: doc
|
||||
cd doc/latex;
|
||||
make;
|
||||
mv refman.pdf ..;
|
||||
cd ../..;
|
||||
rm -r doc/latex;
|
||||
|
||||
|
||||
#
|
||||
# This target runs both the low level code tests and the high level script tests.
|
||||
#
|
||||
|
||||
test: $(PROGRAMS) fish_tests
|
||||
./fish_tests; cd tests; ../fish <test.fish;
|
||||
.PHONY: test
|
||||
|
||||
|
||||
#
|
||||
# commands.hdr collects documentation on all commands, functions and
|
||||
# builtins
|
||||
#
|
||||
|
||||
doc_src/commands.hdr:$(HELP_SRC) doc_src/commands.hdr.in
|
||||
-rm command_list.tmp $@
|
||||
for i in `printf "%s\n" $(HELP_SRC)|sort`; do \
|
||||
echo "<hr>" >>command_list.tmp; \
|
||||
cat $$i >>command_list.tmp; \
|
||||
echo >>command_list.tmp; \
|
||||
echo >>command_list.tmp; \
|
||||
echo "Back to <a href='index.html#toc-commands'>index</a>". >>command_list.tmp; \
|
||||
done
|
||||
mv command_list.tmp command_list.txt
|
||||
cat $@.in | awk '{if ($$0 ~ /@command_list@/){ system("cat command_list.txt");} else{ print $$0;}}' >$@
|
||||
|
||||
|
||||
toc.txt: $(HDR_FILES:index.hdr=index.hdr.in)
|
||||
-rm toc.tmp $@
|
||||
for i in $(HDR_FILES:index.hdr=index.hdr.in); do\
|
||||
NAME=`basename $$i .hdr`; \
|
||||
NAME=`basename $$NAME .hdr.in`; \
|
||||
sed <$$i >>toc.tmp -n \
|
||||
-e 's,.*\\page *\([^ ]*\) *\(.*\)$$,- <a href="'$$NAME'.html" id="toc-'$$NAME'">\2</a>,p' \
|
||||
-e 's,.*\\section *\([^ ]*\) *\(.*\)$$, - <a href="'$$NAME'.html#\1">\2</a>,p'; \
|
||||
done
|
||||
mv toc.tmp $@
|
||||
|
||||
doc_src/index.hdr: toc.txt doc_src/index.hdr.in
|
||||
cat $@.in | awk '{if ($$0 ~ /@toc@/){ system("cat toc.txt");} else{ print $$0;}}' >$@
|
||||
|
||||
|
||||
#
|
||||
# doc.h is a compilation of the various snipptes of text used both for
|
||||
# the user documentation and for internal help functions into a single
|
||||
# file that can be parsed dy Doxygen to generate the user
|
||||
# documentation.
|
||||
#
|
||||
|
||||
doc.h: $(HDR_FILES)
|
||||
cat $(HDR_FILES) >$@
|
||||
|
||||
#
|
||||
# This rule creates complete doxygen headers from each of the various
|
||||
# snipptes of text used both for the user documentation and for
|
||||
# internal help functions, that can be parsed to Doxygen to generate
|
||||
# the internal help function text.
|
||||
#
|
||||
|
||||
%.doxygen:%.txt
|
||||
echo "/** \page " `basename $*` >$@;
|
||||
cat $*.txt >>$@;
|
||||
echo "*/" >>$@
|
||||
|
||||
%: %.in
|
||||
sed <$@.in >$@ \
|
||||
-e "s,@sysconfdir\@,$(sysconfdir),g" \
|
||||
-e "s,@datadir\@,$(datadir),g" \
|
||||
-e "s,@docdir\@,$(docdir),g" \
|
||||
-e "s|@configure_input\@|$@, generated from $@.in by the Makefile. DO NOT MANUALLY EDIT THIS FILE!|g" \
|
||||
-e "s,@prefix\@,$(prefix),g" \
|
||||
-e "s,@optbindirs\@,$(optbindirs),g"
|
||||
#-e "s,@\@,$(),"
|
||||
|
||||
|
||||
#
|
||||
# Compile translation files to binary format
|
||||
#
|
||||
|
||||
%.gmo:
|
||||
if test "$(HAVE_GETTEXT)" = 1; then \
|
||||
msgfmt -o $*.gmo $*.po; \
|
||||
fi
|
||||
|
||||
|
||||
#
|
||||
# Update existing po file or copy messages.pot
|
||||
#
|
||||
|
||||
%.po:messages.pot
|
||||
if test $(HAVE_GETTEXT) = 1;then \
|
||||
if test -f $*.po; then \
|
||||
msgmerge -U --backup=existing $*.po messages.pot;\
|
||||
else \
|
||||
cp messages.pot $*.po;\
|
||||
fi; \
|
||||
fi
|
||||
|
||||
|
||||
#
|
||||
# Create a template translation object
|
||||
#
|
||||
|
||||
messages.pot: *.cpp *.h share/completions/*.fish share/functions/*.fish
|
||||
if test $(HAVE_GETTEXT) = 1;then \
|
||||
xgettext -k_ -kN_ *.cpp *.h -o messages.pot; \
|
||||
if xgettext -j -k_ -kN_ -k--description -LShell share/completions/*.fish share/functions/*.fish -o messages.pot; then true; else \
|
||||
echo "Your xgettext version is too old to build the messages.pot file"\
|
||||
rm messages.pot\
|
||||
false;\
|
||||
fi; \
|
||||
fi
|
||||
|
||||
builtin.o: $(BUILTIN_FILES)
|
||||
|
||||
common.o: $(COMMON_FILES)
|
||||
|
||||
|
||||
#
|
||||
# Generate the internal help functions by making doxygen create
|
||||
# man-pages. The convertion path looks like this:
|
||||
#
|
||||
# .txt file
|
||||
# ||
|
||||
# (make)
|
||||
# ||
|
||||
# \/
|
||||
# .doxygen file
|
||||
# ||
|
||||
# (doxygen)
|
||||
# ||
|
||||
# \/
|
||||
# roff file
|
||||
# ||
|
||||
# (__fish_print_help)
|
||||
# ||
|
||||
# \/
|
||||
# formated text
|
||||
# with escape
|
||||
# sequences
|
||||
#
|
||||
#
|
||||
# There ought to be something simpler.
|
||||
#
|
||||
|
||||
share/man: $(HELP_SRC)
|
||||
-mkdir share/man
|
||||
touch share/man
|
||||
-rm -Rf share/man/man1
|
||||
./build_tools/build_documentation.sh Doxyfile.help ./doc_src ./share
|
||||
|
||||
#
|
||||
# The build rules for installing/uninstalling fish
|
||||
#
|
||||
|
||||
#
|
||||
# Check for an incompatible installed fish version, and fail with an
|
||||
# error if found
|
||||
#
|
||||
|
||||
check-uninstall:
|
||||
if test -f $(DESTDIR)$(sysconfdir)/fish.d/fish_function.fish -o -f $(DESTDIR)$(sysconfdir)/fish.d/fish_complete.fish; then \
|
||||
echo;\
|
||||
echo ERROR;\
|
||||
echo;\
|
||||
echo An older fish installation using an incompatible filesystem hierarchy was detected;\
|
||||
echo You must uninstall this fish version before proceeding;\
|
||||
echo type \'$(MAKE) uninstall-legacy\' to uninstall these files,;\
|
||||
echo or type \'$(MAKE) force-install\' to force installation.;\
|
||||
echo The latter may result in a broken installation.;\
|
||||
echo;\
|
||||
false;\
|
||||
fi;
|
||||
if test -f $(DESTDIR)$(sysconfdir)/fish; then \
|
||||
echo;\
|
||||
echo ERROR;\
|
||||
echo;\
|
||||
echo An older fish installation using an incompatible filesystem hierarchy was detected;\
|
||||
echo You must remove the file $(DESTDIR)$(sysconfdir)/fish before proceeding;\
|
||||
echo type \'$(MAKE) uninstall-legacy\' to uninstall this file,;\
|
||||
echo or remove it manually using \'rm $(DESTDIR)$(sysconfdir)/fish\'.;\
|
||||
echo;\
|
||||
false;\
|
||||
fi;
|
||||
.PHONY: check-uninstall
|
||||
|
||||
check-legacy-binaries:
|
||||
@SEQLOC=$(prefix)/bin/seq;\
|
||||
if test -f "$$SEQLOC" && grep -q '\(^#!/.*/fish\|^#!/usr/bin/env fish\)' "$$SEQLOC"; then\
|
||||
echo "An outdated seq from a previous fish install was found. You should remove it with:";\
|
||||
echo " rm '$$SEQLOC'";\
|
||||
fi;
|
||||
@SETCOLOR_LOC=$(prefix)/bin/set_color;\
|
||||
if test -x "$$SETCOLOR_LOC" && $$SETCOLOR_LOC -v 2>&1 >/dev/null | grep -q "^set_color, version "; then\
|
||||
echo "An outdated set_color from a previous fish install was found. You should remove it with:";\
|
||||
echo " rm '$$SETCOLOR_LOC'";\
|
||||
fi;
|
||||
@true;
|
||||
.PHONY: check-legacy-binaries
|
||||
|
||||
|
||||
#
|
||||
# This check makes sure that the install-sh script is executable. The
|
||||
# darcs repo doesn't preserve the executable bit, so this needs to be
|
||||
# run after checkout.
|
||||
#
|
||||
|
||||
install-sh:
|
||||
if test -x $@; then true; else chmod 755 $@; fi
|
||||
.PHONY: install-sh
|
||||
|
||||
|
||||
#
|
||||
# Try to install after checking for incompatible installed versions.
|
||||
#
|
||||
|
||||
install: all install-sh check-uninstall install-force check-legacy-binaries
|
||||
.PHONY: install
|
||||
|
||||
#
|
||||
# Xcode install
|
||||
#
|
||||
xcode-install:
|
||||
rm -Rf /tmp/fish_build;\
|
||||
xcodebuild install DSTROOT=/tmp/fish_build;\
|
||||
ditto /tmp/fish_build /
|
||||
.PHONY: xcode-install
|
||||
|
||||
#
|
||||
# Force installation, even in presense of incompatible previous
|
||||
# version. This may fail.
|
||||
# These 'true' lines are to prevent installs from failing for (e.g.) missing man pages.
|
||||
#
|
||||
|
||||
install-force: all install-translations
|
||||
$(INSTALL) -m 755 -d $(DESTDIR)$(bindir)
|
||||
for i in $(PROGRAMS); do\
|
||||
$(INSTALL) -m 755 $$i $(DESTDIR)$(bindir) ; \
|
||||
true ;\
|
||||
done;
|
||||
$(INSTALL) -m 755 -d $(DESTDIR)$(sysconfdir)/fish
|
||||
$(INSTALL) -m 755 -d $(DESTDIR)$(datadir)/fish
|
||||
$(INSTALL) -m 755 -d $(DESTDIR)$(datadir)/fish/completions
|
||||
$(INSTALL) -m 755 -d $(DESTDIR)$(datadir)/fish/functions
|
||||
$(INSTALL) -m 755 -d $(DESTDIR)$(datadir)/fish/man/man1
|
||||
$(INSTALL) -m 755 -d $(DESTDIR)$(datadir)/fish/tools
|
||||
$(INSTALL) -m 755 -d $(DESTDIR)$(datadir)/fish/tools/web_config
|
||||
$(INSTALL) -m 755 -d $(DESTDIR)$(datadir)/fish/tools/web_config/sample_prompts
|
||||
$(INSTALL) -m 644 etc/config.fish $(DESTDIR)$(sysconfdir)/fish/
|
||||
$(INSTALL) -m 644 share/config.fish $(DESTDIR)$(datadir)/fish/
|
||||
for i in $(COMPLETIONS_DIR_FILES:%='%'); do \
|
||||
$(INSTALL) -m 644 $$i $(DESTDIR)$(datadir)/fish/completions/; \
|
||||
true; \
|
||||
done;
|
||||
for i in $(FUNCTIONS_DIR_FILES:%='%'); do \
|
||||
$(INSTALL) -m 644 $$i $(DESTDIR)$(datadir)/fish/functions/; \
|
||||
true; \
|
||||
done;
|
||||
for i in share/man/man1/*.1; do \
|
||||
$(INSTALL) -m 644 $$i $(DESTDIR)$(datadir)/fish/man/man1/; \
|
||||
true; \
|
||||
done;
|
||||
for i in share/tools/*.py; do\
|
||||
$(INSTALL) -m 755 $$i $(DESTDIR)$(datadir)/fish/tools/; \
|
||||
true; \
|
||||
done;
|
||||
for i in share/tools/web_config/*; do\
|
||||
$(INSTALL) -m 644 $$i $(DESTDIR)$(datadir)/fish/tools/web_config/; \
|
||||
true; \
|
||||
done;
|
||||
for i in share/tools/web_config/sample_prompts/*.fish; do\
|
||||
$(INSTALL) -m 644 $$i $(DESTDIR)$(datadir)/fish/tools/web_config/sample_prompts/; \
|
||||
true; \
|
||||
done;
|
||||
for i in share/tools/web_config/*.py; do\
|
||||
$(INSTALL) -m 755 $$i $(DESTDIR)$(datadir)/fish/tools/web_config/; \
|
||||
true; \
|
||||
done;
|
||||
|
||||
$(INSTALL) -m 755 -d $(DESTDIR)$(docdir)
|
||||
for i in user_doc/html/* ChangeLog; do \
|
||||
if test -f $$i; then \
|
||||
$(INSTALL) -m 644 $$i $(DESTDIR)$(docdir); \
|
||||
fi; \
|
||||
done;
|
||||
$(INSTALL) -m 755 -d $(DESTDIR)$(mandir)/man1
|
||||
for i in $(MANUALS); do \
|
||||
$(INSTALL) -m 644 $$i $(DESTDIR)$(mandir)/man1/; \
|
||||
true; \
|
||||
done;
|
||||
@echo fish is now installed on your system.
|
||||
@echo To run fish, type \'fish\' in your terminal.
|
||||
@echo
|
||||
@echo To use fish as your login shell:
|
||||
@grep -q -- "$(DESTDIR)$(bindir)/fish" /etc/shells || echo \* add the line \'$(DESTDIR)$(bindir)/fish\' to the file \'/etc/shells\'.
|
||||
@echo \* use the command \'chsh -s $(DESTDIR)$(bindir)/fish\'.
|
||||
@echo
|
||||
@echo To set your colors, run \'fish_config\'
|
||||
@echo To scan your man pages for completions, run \'fish_update_completions\'
|
||||
@echo To autocomplete command suggestions press Ctrl + F or right arrow key.
|
||||
@echo
|
||||
@echo Have fun!
|
||||
.PHONY: install-force
|
||||
|
||||
|
||||
#
|
||||
# Uninstall this fish version
|
||||
#
|
||||
|
||||
uninstall: uninstall-translations
|
||||
-for i in $(PROGRAMS); do \
|
||||
rm -f $(DESTDIR)$(bindir)/$$i; \
|
||||
done;
|
||||
-rm -rf $(DESTDIR)$(sysconfdir)/fish
|
||||
-if test -d $(DESTDIR)$(datadir)/fish; then \
|
||||
rm -r $(DESTDIR)$(datadir)/fish; \
|
||||
fi
|
||||
-if test -d $(DESTDIR)$(docdir); then \
|
||||
rm -rf $(DESTDIR)$(docdir);\
|
||||
fi
|
||||
-for i in $(MANUALS); do \
|
||||
rm -rf $(DESTDIR)$(mandir)/man1/`basename $$i`*; \
|
||||
done;
|
||||
.PHONY: uninstall
|
||||
|
||||
|
||||
#
|
||||
# Uninstall an older fish release. This is not the default uninstall
|
||||
# since there is a slight chance that it removes a file put in place by
|
||||
# the sysadmin. But if 'make install' detects a file confligt, it
|
||||
# suggests using this target.
|
||||
#
|
||||
|
||||
uninstall-legacy: uninstall
|
||||
-rm -f $(DESTDIR)$(sysconfdir)/fish.d/fish_interactive.fish
|
||||
-rm -f $(DESTDIR)$(sysconfdir)/fish.d/fish_complete.fish
|
||||
-rm -f $(DESTDIR)$(sysconfdir)/fish.d/fish_function.fish
|
||||
-rm -f $(DESTDIR)$(sysconfdir)/fish/fish_inputrc
|
||||
-if test -d $(DESTDIR)$(sysconfdir)/fish.d/completions; then \
|
||||
for i in $(COMPLETIONS_DIR_FILES); do \
|
||||
basename=`basename $$i`; \
|
||||
if test -f $(DESTDIR)$(sysconfdir)/fish.d/completions/$$basename; then \
|
||||
rm $(DESTDIR)$(sysconfdir)/fish.d/completions/$$basename; \
|
||||
fi; \
|
||||
done; \
|
||||
fi;
|
||||
-rmdir $(DESTDIR)$(sysconfdir)/fish.d/completions
|
||||
-rmdir $(DESTDIR)$(sysconfdir)/fish.d
|
||||
-rm $(DESTDIR)$(sysconfdir)/fish
|
||||
@echo The previous fish installation has been removed.
|
||||
.PHONY: uninstall-legacy
|
||||
|
||||
install-translations: $(TRANSLATIONS)
|
||||
if test $(HAVE_GETTEXT) = 1; then \
|
||||
for i in $(TRANSLATIONS); do \
|
||||
$(INSTALL) -m 755 -d $(DESTDIR)$(datadir)/locale/`basename $$i .gmo`/LC_MESSAGES; \
|
||||
$(INSTALL) -m 644 $$i $(DESTDIR)$(datadir)/locale/`basename $$i .gmo`/LC_MESSAGES/fish.mo; \
|
||||
echo $(DESTDIR)$(datadir)/locale/`basename $$i .gmo`/LC_MESSAGES/fish.mo;\
|
||||
done; \
|
||||
fi;
|
||||
.PHONY: install-translations
|
||||
|
||||
uninstall-translations:
|
||||
if test $(HAVE_GETTEXT) = 1; then \
|
||||
for i in $(TRANSLATIONS_SRC); do \
|
||||
rm -f $(DESTDIR)$(datadir)/locale/*/LC_MESSAGES/fish.mo; \
|
||||
done; \
|
||||
fi
|
||||
.PHONY: uninstall-translations
|
||||
|
||||
|
||||
#
|
||||
# The build rules for all the commands
|
||||
#
|
||||
|
||||
#
|
||||
# Build the fish program.
|
||||
#
|
||||
|
||||
fish: $(FISH_OBJS) fish.o
|
||||
$(CXX) $(FISH_OBJS) fish.o $(LDFLAGS_FISH) -o $@
|
||||
|
||||
|
||||
#
|
||||
# Build the fish_pager program.
|
||||
#
|
||||
|
||||
fish_pager: $(FISH_PAGER_OBJS)
|
||||
$(CXX) $(FISH_PAGER_OBJS) $(LDFLAGS_FISH_PAGER) -o $@
|
||||
|
||||
|
||||
#
|
||||
# Build the fishd program.
|
||||
#
|
||||
|
||||
fishd: $(FISHD_OBJS)
|
||||
$(CXX) $(FISHD_OBJS) $(LDFLAGS_FISHD) -o $@
|
||||
|
||||
|
||||
#
|
||||
# Build the fish_tests program.
|
||||
#
|
||||
|
||||
fish_tests: $(FISH_TESTS_OBJS)
|
||||
$(CXX) $(FISH_TESTS_OBJS) $(LDFLAGS_FISH) -o $@
|
||||
|
||||
|
||||
#
|
||||
# Build the mimedb program.
|
||||
#
|
||||
# mimedb does not need any libraries, so we don't use LDFLAGS here.
|
||||
#
|
||||
|
||||
mimedb: $(MIME_OBJS)
|
||||
$(CXX) $(MIME_OBJS) $(LDFLAGS_MIMEDB) -o $@
|
||||
|
||||
|
||||
#
|
||||
# Build the fish_indent program.
|
||||
#
|
||||
|
||||
fish_indent: $(FISH_INDENT_OBJS)
|
||||
$(CXX) $(FISH_INDENT_OBJS) $(LDFLAGS_FISH_INDENT) -o $@
|
||||
|
||||
|
||||
#
|
||||
# Neat little program to show output from terminal
|
||||
#
|
||||
|
||||
key_reader: key_reader.o input_common.o common.o env_universal.o env_universal_common.o wutil.o iothread.o
|
||||
$(CXX) key_reader.o input_common.o common.o env_universal.o env_universal_common.o wutil.o iothread.o $(LDFLAGS_FISH) -o $@
|
||||
|
||||
|
||||
#
|
||||
# Update dependencies
|
||||
#
|
||||
depend:
|
||||
makedepend -fMakefile.in -Y *.cpp
|
||||
./config.status
|
||||
.PHONY: depend
|
||||
|
||||
#
|
||||
# Make compressed tar archives
|
||||
#
|
||||
|
||||
fish-@PACKAGE_VERSION@.tar.gz: fish-@PACKAGE_VERSION@.tar
|
||||
gzip -f --best -c fish-@PACKAGE_VERSION@.tar >fish-@PACKAGE_VERSION@.tar.gz
|
||||
|
||||
fish-@PACKAGE_VERSION@.tar.bz2: fish-@PACKAGE_VERSION@.tar
|
||||
bzip2 -f --best -k fish-@PACKAGE_VERSION@.tar
|
||||
|
||||
dist: fish-@PACKAGE_VERSION@.tar.bz2
|
||||
.PHONY: dist
|
||||
|
||||
#
|
||||
# Build the RPM spec file.
|
||||
#
|
||||
|
||||
fish.spec: fish.spec.in
|
||||
./config.status
|
||||
|
||||
|
||||
|
||||
#
|
||||
# Create .rpm file for the current systems architecture and an
|
||||
# .src.rpm file.
|
||||
#
|
||||
|
||||
rpm: fish-@PACKAGE_VERSION@.tar.bz2 fish.spec
|
||||
@if which rpmbuild; then true; else \
|
||||
echo Could not find the rpmbuild command, needed to build an rpm; \
|
||||
echo You may be able to install it using the following command:; \
|
||||
echo \'yum install rpm-build\'; \
|
||||
false; \
|
||||
fi
|
||||
cp fish.spec /usr/src/redhat/SPECS/
|
||||
cp fish-@PACKAGE_VERSION@.tar.bz2 /usr/src/redhat/SOURCES/
|
||||
rpmbuild -ba --clean /usr/src/redhat/SPECS/fish.spec
|
||||
mv /usr/src/redhat/RPMS/*/fish*@PACKAGE_VERSION@*.rpm .
|
||||
mv /usr/src/redhat/SRPMS/fish*@PACKAGE_VERSION@*.src.rpm .
|
||||
.PHONY: rpm
|
||||
|
||||
|
||||
#
|
||||
# Cleanup targets
|
||||
#
|
||||
|
||||
#
|
||||
# distclean should restore the tree to the state right after extracting a tarball.
|
||||
#
|
||||
|
||||
distclean: clean
|
||||
rm -f fish.spec
|
||||
rm -f config.status config.log config.h Makefile
|
||||
.PHONY: distclean
|
||||
|
||||
|
||||
#
|
||||
# clean removes everything built by the makefile, but not things that
|
||||
# are created by the configure script.
|
||||
#
|
||||
|
||||
# Don't delete the docs unless we have Doxygen installed
|
||||
# We provide pre-built docs in the tarball, and if they get
|
||||
# deleted we won't be able to regenerate them
|
||||
|
||||
clean:
|
||||
rm -f *.o doc.h doc.tmp doc_src/*.doxygen doc_src/*.cpp doc_src/*.o doc_src/commands.hdr
|
||||
rm -f $(GENERATED_INTERN_SCRIPT_FILES)
|
||||
rm -f tests/tmp.err tests/tmp.out tests/tmp.status tests/foo.txt
|
||||
rm -f $(PROGRAMS) fish_tests key_reader
|
||||
rm -f command_list.txt toc.txt
|
||||
rm -f doc_src/index.hdr doc_src/commands.hdr
|
||||
rm -f fish-@PACKAGE_VERSION@.tar
|
||||
rm -f fish-@PACKAGE_VERSION@.tar.gz
|
||||
rm -f fish-@PACKAGE_VERSION@.tar.bz2
|
||||
if command -v doxygen; then \
|
||||
rm -rf doc user_doc share/man; \
|
||||
fi
|
||||
rm -rf fish-@PACKAGE_VERSION@
|
||||
rm -f $(TRANSLATIONS)
|
||||
.PHONY: clean
|
||||
|
||||
|
||||
# DO NOT DELETE THIS LINE -- make depend depends on it.
|
||||
|
||||
autoload.o: config.h autoload.h common.h util.h lru.h wutil.h signal.h env.h
|
||||
autoload.o: exec.h proc.h io.h
|
||||
builtin.o: config.h signal.h fallback.h util.h wutil.h common.h builtin.h
|
||||
builtin.o: io.h function.h event.h complete.h proc.h parser.h reader.h env.h
|
||||
builtin.o: wgetopt.h sanity.h tokenizer.h wildcard.h expand.h input_common.h
|
||||
builtin.o: input.h intern.h exec.h highlight.h screen.h color.h parse_util.h
|
||||
builtin.o: autoload.h lru.h parser_keywords.h path.h history.h
|
||||
builtin.o: builtin_set.cpp builtin_commandline.cpp builtin_complete.cpp
|
||||
builtin.o: builtin_ulimit.cpp builtin_jobs.cpp builtin_printf.cpp
|
||||
builtin_commandline.o: config.h signal.h fallback.h util.h wutil.h common.h
|
||||
builtin_commandline.o: builtin.h io.h wgetopt.h reader.h complete.h proc.h
|
||||
builtin_commandline.o: parser.h event.h function.h tokenizer.h input_common.h
|
||||
builtin_commandline.o: input.h parse_util.h autoload.h lru.h
|
||||
builtin_complete.o: config.h signal.h fallback.h util.h wutil.h common.h
|
||||
builtin_complete.o: builtin.h io.h complete.h wgetopt.h parser.h proc.h
|
||||
builtin_complete.o: event.h function.h reader.h
|
||||
builtin_jobs.o: config.h fallback.h signal.h util.h wutil.h common.h
|
||||
builtin_jobs.o: builtin.h io.h proc.h parser.h event.h function.h wgetopt.h
|
||||
builtin_set.o: config.h signal.h fallback.h util.h wutil.h common.h builtin.h
|
||||
builtin_set.o: io.h env.h expand.h wgetopt.h proc.h parser.h event.h
|
||||
builtin_set.o: function.h
|
||||
builtin_test.o: config.h common.h util.h builtin.h io.h wutil.h proc.h
|
||||
builtin_test.o: signal.h
|
||||
builtin_ulimit.o: config.h fallback.h signal.h util.h builtin.h io.h common.h
|
||||
builtin_ulimit.o: wgetopt.h
|
||||
builtin_printf.o: wgetopt.h
|
||||
color.o: color.h config.h common.h util.h fallback.h signal.h
|
||||
common.o: config.h fallback.h signal.h util.h wutil.h common.h expand.h
|
||||
common.o: proc.h io.h wildcard.h parser.h event.h function.h complete.h
|
||||
common.o: util.cpp fallback.cpp
|
||||
complete.o: config.h signal.h fallback.h util.h tokenizer.h common.h
|
||||
complete.o: wildcard.h expand.h proc.h io.h parser.h event.h function.h
|
||||
complete.o: complete.h builtin.h env.h exec.h reader.h history.h wutil.h
|
||||
complete.o: intern.h parse_util.h autoload.h lru.h parser_keywords.h path.h
|
||||
env.o: config.h signal.h fallback.h util.h wutil.h common.h proc.h io.h env.h
|
||||
env.o: sanity.h expand.h history.h reader.h complete.h parser.h event.h
|
||||
env.o: function.h env_universal.h env_universal_common.h input.h
|
||||
env.o: input_common.h path.h
|
||||
env_universal.o: config.h signal.h fallback.h util.h common.h wutil.h
|
||||
env_universal.o: env_universal_common.h env_universal.h
|
||||
env_universal_common.o: config.h signal.h fallback.h util.h common.h wutil.h
|
||||
env_universal_common.o: env_universal_common.h
|
||||
event.o: config.h signal.h fallback.h util.h wutil.h common.h function.h
|
||||
event.o: event.h proc.h io.h parser.h
|
||||
exec.o: config.h signal.h fallback.h util.h iothread.h postfork.h common.h
|
||||
exec.o: proc.h io.h wutil.h exec.h parser.h event.h function.h builtin.h
|
||||
exec.o: env.h wildcard.h expand.h sanity.h parse_util.h autoload.h lru.h
|
||||
expand.o: config.h signal.h fallback.h util.h common.h wutil.h env.h proc.h
|
||||
expand.o: io.h parser.h event.h function.h expand.h wildcard.h exec.h
|
||||
expand.o: tokenizer.h complete.h parse_util.h autoload.h lru.h
|
||||
fallback.o: config.h fallback.h signal.h util.h
|
||||
fish.o: config.h signal.h fallback.h util.h common.h reader.h io.h complete.h
|
||||
fish.o: builtin.h function.h event.h wutil.h env.h sanity.h proc.h parser.h
|
||||
fish.o: expand.h intern.h exec.h output.h screen.h color.h history.h path.h
|
||||
fish_indent.o: config.h fallback.h signal.h util.h common.h wutil.h
|
||||
fish_indent.o: tokenizer.h print_help.h parser_keywords.h
|
||||
fish_pager.o: config.h signal.h fallback.h util.h wutil.h common.h complete.h
|
||||
fish_pager.o: output.h screen.h color.h input_common.h env_universal.h
|
||||
fish_pager.o: env_universal_common.h print_help.h
|
||||
fish_tests.o: config.h signal.h fallback.h util.h common.h proc.h io.h
|
||||
fish_tests.o: reader.h complete.h builtin.h function.h event.h autoload.h
|
||||
fish_tests.o: lru.h wutil.h env.h expand.h parser.h tokenizer.h output.h
|
||||
fish_tests.o: screen.h color.h exec.h path.h history.h highlight.h iothread.h
|
||||
fish_tests.o: postfork.h
|
||||
fishd.o: config.h signal.h fallback.h util.h common.h wutil.h
|
||||
fishd.o: env_universal_common.h path.h env.h print_help.h
|
||||
function.o: config.h signal.h wutil.h common.h util.h fallback.h function.h
|
||||
function.o: event.h proc.h io.h parser.h intern.h reader.h complete.h
|
||||
function.o: parse_util.h autoload.h lru.h parser_keywords.h env.h expand.h
|
||||
highlight.o: config.h signal.h fallback.h util.h wutil.h common.h highlight.h
|
||||
highlight.o: env.h screen.h color.h tokenizer.h proc.h io.h parser.h event.h
|
||||
highlight.o: function.h parse_util.h autoload.h lru.h parser_keywords.h
|
||||
highlight.o: builtin.h expand.h sanity.h complete.h output.h wildcard.h
|
||||
highlight.o: path.h history.h
|
||||
history.o: config.h fallback.h signal.h util.h sanity.h tokenizer.h common.h
|
||||
history.o: wutil.h history.h intern.h path.h env.h autoload.h lru.h
|
||||
history.o: iothread.h
|
||||
input.o: config.h signal.h fallback.h util.h wutil.h common.h reader.h io.h
|
||||
input.o: complete.h proc.h sanity.h input_common.h input.h parser.h event.h
|
||||
input.o: function.h env.h expand.h output.h screen.h color.h intern.h
|
||||
input_common.o: config.h fallback.h signal.h util.h common.h wutil.h
|
||||
input_common.o: input_common.h env_universal.h env_universal_common.h
|
||||
input_common.o: iothread.h
|
||||
intern.o: config.h fallback.h signal.h util.h wutil.h common.h intern.h
|
||||
io.o: config.h fallback.h signal.h util.h wutil.h common.h exec.h proc.h io.h
|
||||
iothread.o: config.h iothread.h common.h util.h signal.h
|
||||
key_reader.o: config.h common.h util.h fallback.h signal.h input_common.h
|
||||
kill.o: config.h signal.h fallback.h util.h wutil.h common.h kill.h proc.h
|
||||
kill.o: io.h sanity.h env.h exec.h path.h
|
||||
mimedb.o: config.h xdgmime.h fallback.h signal.h util.h print_help.h
|
||||
output.o: config.h signal.h fallback.h util.h wutil.h common.h expand.h
|
||||
output.o: output.h screen.h color.h highlight.h env.h
|
||||
parse_util.o: config.h fallback.h signal.h util.h wutil.h common.h
|
||||
parse_util.o: tokenizer.h parse_util.h autoload.h lru.h expand.h intern.h
|
||||
parse_util.o: exec.h proc.h io.h env.h wildcard.h
|
||||
parser.o: config.h signal.h fallback.h util.h common.h wutil.h proc.h io.h
|
||||
parser.o: parser.h event.h function.h parser_keywords.h tokenizer.h exec.h
|
||||
parser.o: wildcard.h expand.h builtin.h env.h reader.h complete.h sanity.h
|
||||
parser.o: env_universal.h env_universal_common.h intern.h parse_util.h
|
||||
parser.o: autoload.h lru.h path.h
|
||||
parser_keywords.o: config.h fallback.h signal.h common.h util.h
|
||||
parser_keywords.o: parser_keywords.h
|
||||
path.o: config.h fallback.h signal.h util.h common.h env.h wutil.h path.h
|
||||
path.o: expand.h
|
||||
postfork.o: signal.h postfork.h config.h common.h util.h proc.h io.h wutil.h
|
||||
postfork.o: iothread.h exec.h
|
||||
print_help.o: print_help.h
|
||||
proc.o: config.h signal.h fallback.h util.h wutil.h common.h proc.h io.h
|
||||
proc.o: reader.h complete.h sanity.h env.h parser.h event.h function.h
|
||||
proc.o: output.h screen.h color.h
|
||||
reader.o: config.h signal.h fallback.h util.h wutil.h common.h highlight.h
|
||||
reader.o: env.h screen.h color.h reader.h io.h complete.h proc.h parser.h
|
||||
reader.o: event.h function.h history.h sanity.h exec.h expand.h tokenizer.h
|
||||
reader.o: kill.h input_common.h input.h output.h iothread.h intern.h path.h
|
||||
reader.o: parse_util.h autoload.h lru.h
|
||||
sanity.o: config.h signal.h fallback.h util.h common.h sanity.h proc.h io.h
|
||||
sanity.o: history.h wutil.h reader.h complete.h kill.h
|
||||
screen.o: config.h fallback.h signal.h common.h util.h wutil.h output.h
|
||||
screen.o: screen.h color.h highlight.h env.h
|
||||
signal.o: config.h signal.h common.h util.h fallback.h wutil.h event.h
|
||||
signal.o: reader.h io.h complete.h proc.h
|
||||
tokenizer.o: config.h fallback.h signal.h util.h wutil.h common.h tokenizer.h
|
||||
util.o: config.h fallback.h signal.h util.h common.h wutil.h
|
||||
wgetopt.o: config.h wgetopt.h wutil.h common.h util.h fallback.h signal.h
|
||||
wildcard.o: config.h fallback.h signal.h util.h wutil.h common.h complete.h
|
||||
wildcard.o: wildcard.h expand.h reader.h io.h exec.h proc.h
|
||||
wutil.o: config.h fallback.h signal.h util.h common.h wutil.h
|
||||
xdgmime.o: xdgmime.h xdgmimeint.h xdgmimeglob.h xdgmimemagic.h xdgmimealias.h
|
||||
xdgmime.o: xdgmimeparent.h
|
||||
xdgmimealias.o: xdgmimealias.h xdgmime.h xdgmimeint.h
|
||||
xdgmimeglob.o: xdgmimeglob.h xdgmime.h xdgmimeint.h
|
||||
xdgmimeint.o: xdgmimeint.h xdgmime.h
|
||||
xdgmimemagic.o: xdgmimemagic.h xdgmime.h xdgmimeint.h
|
||||
xdgmimeparent.o: xdgmimeparent.h xdgmime.h xdgmimeint.h
|
||||
71
README.md
Normal file
71
README.md
Normal file
@@ -0,0 +1,71 @@
|
||||
[fish](http://ridiculousfish.com/shell/) - the friendly interactive shell
|
||||
================================================
|
||||
|
||||
fish is a smart and user-friendly command line shell for OS X, Linux, and the rest of the family. fish includes features like syntax highlighting, autosuggest-as-you-type, and fancy tab completions that just work, with no configuration required.
|
||||
|
||||
For more on fish's design philosophy, see the [design document](http://ridiculousfish.com/shell/user_doc/html/design.html).
|
||||
|
||||
## Quick Start
|
||||
|
||||
fish generally works like other shells, like bash or zsh. A few important differences are documented at <http://ridiculousfish.com/shell/faq.html>
|
||||
|
||||
Detailed user documentation is available by running `help` within fish, and also at <http://ridiculousfish.com/shell/user_doc/html/>
|
||||
|
||||
## Building
|
||||
|
||||
fish is written in a sane subset of C++98, with a few components from C++TR1. It builds successfully with g++ 4.2 or later, and with clang. It also will build as C++11.
|
||||
|
||||
fish can be built using autotools or Xcode.
|
||||
|
||||
### Autotools Build
|
||||
|
||||
autoconf
|
||||
./configure
|
||||
make [gmake on BSD]
|
||||
sudo make install
|
||||
|
||||
### Xcode Development Build
|
||||
|
||||
* Build the `base` target in Xcode
|
||||
* Run the fish executable, for example, in `DerivedData/fish/Build/Products/Debug/base/bin/fish`
|
||||
|
||||
### Xcode Build and Install
|
||||
|
||||
xcodebuild install
|
||||
sudo ditto /tmp/fish.dst /
|
||||
|
||||
## Help, it didn't build!
|
||||
|
||||
If fish reports that it could not find curses, try installing a curses development package and build again.
|
||||
|
||||
On Debian or Ubuntu you want:
|
||||
|
||||
sudo apt-get install libncurses5-dev libncursesw5-dev
|
||||
|
||||
on RedHat, CentOS, or Amazon EC2:
|
||||
|
||||
sudo yum install ncurses-devel
|
||||
|
||||
## Packages for Linux
|
||||
|
||||
Nightly builds for several Linux distros can be downloaded from <http://download.opensuse.org/repositories/home:/siteshwar/>
|
||||
|
||||
## Switching to fish
|
||||
|
||||
If you wish to use fish as your default shell, use the following command:
|
||||
|
||||
chsh -s /usr/local/bin/fish
|
||||
|
||||
chsh will prompt you for your password, and change your default shell.
|
||||
|
||||
To switch your default shell back, you can run:
|
||||
|
||||
chsh -s /bin/bash
|
||||
|
||||
Substitute /bin/bash with /bin/tcsh or /bin/zsh as appropriate.
|
||||
|
||||
## Contact Us
|
||||
|
||||
Questions, comments, rants and raves can be posted to the official fish mailing list at <https://lists.sourceforge.net/lists/listinfo/fish-users> or join us on our IRC channel #fish at irc.oftc.net
|
||||
|
||||
Found a bug? Have an awesome idea? Please open an issue on this github page.
|
||||
215
README.rst
215
README.rst
@@ -1,215 +0,0 @@
|
||||
.. |Cirrus CI| image:: https://api.cirrus-ci.com/github/fish-shell/fish-shell.svg?branch=master
|
||||
:target: https://cirrus-ci.com/github/fish-shell/fish-shell
|
||||
:alt: Cirrus CI Build Status
|
||||
|
||||
`fish <https://fishshell.com/>`__ - the friendly interactive shell |Build Status| |Cirrus CI|
|
||||
=============================================================================================
|
||||
|
||||
fish is a smart and user-friendly command line shell for macOS, Linux,
|
||||
and the rest of the family. fish includes features like syntax
|
||||
highlighting, autosuggest-as-you-type, and fancy tab completions that
|
||||
just work, with no configuration required.
|
||||
|
||||
For downloads, screenshots and more, go to https://fishshell.com/.
|
||||
|
||||
Quick Start
|
||||
-----------
|
||||
|
||||
fish generally works like other shells, like bash or zsh. A few
|
||||
important differences can be found at
|
||||
https://fishshell.com/docs/current/tutorial.html by searching for the
|
||||
magic phrase “unlike other shells”.
|
||||
|
||||
Detailed user documentation is available by running ``help`` within
|
||||
fish, and also at https://fishshell.com/docs/current/index.html
|
||||
|
||||
Getting fish
|
||||
------------
|
||||
|
||||
macOS
|
||||
~~~~~
|
||||
|
||||
fish can be installed:
|
||||
|
||||
- using `Homebrew <http://brew.sh/>`__: ``brew install fish``
|
||||
- using `MacPorts <https://www.macports.org/>`__:
|
||||
``sudo port install fish``
|
||||
- using the `installer from fishshell.com <https://fishshell.com/>`__
|
||||
- as a `standalone app from fishshell.com <https://fishshell.com/>`__
|
||||
|
||||
Note: The minimum supported macOS version is 10.10 "Yosemite".
|
||||
|
||||
Packages for Linux
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Packages for Debian, Fedora, openSUSE, and Red Hat Enterprise
|
||||
Linux/CentOS are available from the `openSUSE Build
|
||||
Service <https://software.opensuse.org/download.html?project=shells%3Afish&package=fish>`__.
|
||||
|
||||
Packages for Ubuntu are available from the `fish
|
||||
PPA <https://launchpad.net/~fish-shell/+archive/ubuntu/release-4>`__,
|
||||
and can be installed using the following commands:
|
||||
|
||||
::
|
||||
|
||||
sudo apt-add-repository ppa:fish-shell/release-4
|
||||
sudo apt update
|
||||
sudo apt install fish
|
||||
|
||||
Instructions for other distributions may be found at
|
||||
`fishshell.com <https://fishshell.com>`__.
|
||||
|
||||
Windows
|
||||
~~~~~~~
|
||||
|
||||
- On Windows 10/11, fish can be installed under the WSL Windows Subsystem
|
||||
for Linux with the instructions for the appropriate distribution
|
||||
listed above under “Packages for Linux”, or from source with the
|
||||
instructions below.
|
||||
- Fish can also be installed on all versions of Windows using
|
||||
`Cygwin <https://cygwin.com/>`__ or `MSYS2 <https://github.com/Berrysoft/fish-msys2>`__.
|
||||
|
||||
Building from source
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If packages are not available for your platform, GPG-signed tarballs are
|
||||
available from `fishshell.com <https://fishshell.com/>`__ and
|
||||
`fish-shell on
|
||||
GitHub <https://github.com/fish-shell/fish-shell/releases>`__. See the
|
||||
`Building <#building>`_ section for instructions.
|
||||
|
||||
Running fish
|
||||
------------
|
||||
|
||||
Once installed, run ``fish`` from your current shell to try fish out!
|
||||
|
||||
Dependencies
|
||||
~~~~~~~~~~~~
|
||||
|
||||
Running fish requires:
|
||||
|
||||
- some common \*nix system utilities (currently ``mktemp``), in
|
||||
addition to the basic POSIX utilities (``cat``, ``cut``, ``dirname``,
|
||||
``file``, ``ls``, ``mkdir``, ``mkfifo``, ``rm``, ``sh``, ``sort``, ``tee``, ``tr``,
|
||||
``uname`` and ``sed`` at least, but the full coreutils plus ``find`` and
|
||||
``awk`` is preferred)
|
||||
|
||||
The following optional features also have specific requirements:
|
||||
|
||||
- builtin commands that have the ``--help`` option or print usage
|
||||
messages require ``man`` for display
|
||||
- automated completion generation from manual pages requires Python 3.5+
|
||||
- the ``fish_config`` web configuration tool requires Python 3.5+ and a web browser
|
||||
- system clipboard integration (with the default Ctrl-V and Ctrl-X
|
||||
bindings) require either the ``xsel``, ``xclip``,
|
||||
``wl-copy``/``wl-paste`` or ``pbcopy``/``pbpaste`` utilities
|
||||
- full completions for ``yarn`` and ``npm`` require the
|
||||
``all-the-package-names`` NPM module
|
||||
- ``colorls`` is used, if installed, to add color when running ``ls`` on platforms
|
||||
that do not have color support (such as OpenBSD)
|
||||
|
||||
Building
|
||||
--------
|
||||
|
||||
.. _dependencies-1:
|
||||
|
||||
Dependencies
|
||||
~~~~~~~~~~~~
|
||||
|
||||
Compiling fish requires:
|
||||
|
||||
- Rust (version 1.70 or later)
|
||||
- CMake (version 3.15 or later)
|
||||
- a C compiler (for system feature detection and the test helper binary)
|
||||
- PCRE2 (headers and libraries) - optional, this will be downloaded if missing
|
||||
- gettext (only the msgfmt tool) - optional, for translation support
|
||||
- an Internet connection, as other dependencies will be downloaded automatically
|
||||
|
||||
Sphinx is also optionally required to build the documentation from a
|
||||
cloned git repository.
|
||||
|
||||
Additionally, running the full test suite requires Python 3.5+, tmux, and the pexpect package.
|
||||
|
||||
Building from source with CMake
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Rather than building from source, consider using a packaged build for your platform. Using the
|
||||
steps below makes fish difficult to uninstall or upgrade. Release packages are available from the
|
||||
links above, and up-to-date `development builds of fish are available for many platforms
|
||||
<https://github.com/fish-shell/fish-shell/wiki/Development-builds>`__
|
||||
|
||||
To install into ``/usr/local``, run:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
mkdir build; cd build
|
||||
cmake ..
|
||||
cmake --build .
|
||||
sudo cmake --install .
|
||||
|
||||
The install directory can be changed using the
|
||||
``-DCMAKE_INSTALL_PREFIX`` parameter for ``cmake``.
|
||||
|
||||
CMake Build options
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
In addition to the normal CMake build options (like ``CMAKE_INSTALL_PREFIX``), fish's CMake build has some other options available to customize it.
|
||||
|
||||
- Rust_COMPILER=path - the path to rustc. If not set, cmake will check $PATH and ~/.cargo/bin
|
||||
- Rust_CARGO=path - the path to cargo. If not set, cmake will check $PATH and ~/.cargo/bin
|
||||
- Rust_CARGO_TARGET=target - the target to pass to cargo. Set this for cross-compilation.
|
||||
- BUILD_DOCS=ON|OFF - whether to build the documentation. This is automatically set to OFF when Sphinx isn't installed.
|
||||
- INSTALL_DOCS=ON|OFF - whether to install the docs. This is automatically set to on when BUILD_DOCS is or prebuilt documentation is available (like when building in-tree from a tarball).
|
||||
- FISH_USE_SYSTEM_PCRE2=ON|OFF - whether to use an installed pcre2. This is normally autodetected.
|
||||
- MAC_CODESIGN_ID=String|OFF - the codesign ID to use on Mac, or "OFF" to disable codesigning.
|
||||
- WITH_GETTEXT=ON|OFF - whether to include translations.
|
||||
- extra_functionsdir, extra_completionsdir and extra_confdir - to compile in an additional directory to be searched for functions, completions and configuration snippets
|
||||
|
||||
Building fish with embedded data (experimental)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
You can also build fish with the data files embedded.
|
||||
|
||||
This will include all the datafiles like the included functions or web configuration tool in the main ``fish`` binary.
|
||||
|
||||
Fish will then read these right from its own binary, and print them out when needed. Some files, like the webconfig tool and the manpage completion generator, will be extracted to a temporary directory on-demand. You can list the files with ``status list-files`` and print one with ``status get-file path/to/file`` (e.g. ``status get-file functions/fish_prompt.fish`` to get the default prompt).
|
||||
|
||||
To install fish with embedded files, just use ``cargo``, like::
|
||||
|
||||
cargo install --path /path/to/fish # if you have a git clone
|
||||
cargo install --git https://github.com/fish-shell/fish-shell --tag "$(curl -s https://api.github.com/repos/fish-shell/fish-shell/releases/latest | jq -r .tag_name)" # to build the latest release
|
||||
cargo install --git https://github.com/fish-shell/fish-shell # to build the latest development snapshot
|
||||
|
||||
This will place the standalone binaries in ``~/.cargo/bin/``, but you can place them wherever you want.
|
||||
|
||||
This build won't have the HTML docs (``help`` will open the online version).
|
||||
It will try to build the man pages with sphinx-build. If that is not available and you would like to include man pages, you need to install it and retrigger the build script, e.g. by setting FISH_BUILD_DOCS=1::
|
||||
|
||||
FISH_BUILD_DOCS=1 cargo install --path .
|
||||
|
||||
Setting it to "0" disables the inclusion of man pages.
|
||||
|
||||
To disable translations, disable the ``localize-messages`` feature by passing ``--no-default-features --features=embed-data`` to cargo.
|
||||
|
||||
You can also link this build statically (but not against glibc) and move it to other computers.
|
||||
|
||||
Contributing Changes to the Code
|
||||
--------------------------------
|
||||
|
||||
See the `Guide for Developers <CONTRIBUTING.rst>`__.
|
||||
|
||||
Contact Us
|
||||
----------
|
||||
|
||||
Questions, comments, rants and raves can be posted to the official fish
|
||||
mailing list at https://lists.sourceforge.net/lists/listinfo/fish-users
|
||||
or join us on our `matrix
|
||||
channel <https://matrix.to/#/#fish-shell:matrix.org>`__. Or use the `fish tag
|
||||
on Unix & Linux Stackexchange <https://unix.stackexchange.com/questions/tagged/fish>`__.
|
||||
There is also a fish tag on Stackoverflow, but it is typically a poor fit.
|
||||
|
||||
Found a bug? Have an awesome idea? Please `open an
|
||||
issue <https://github.com/fish-shell/fish-shell/issues/new>`__.
|
||||
|
||||
.. |Build Status| image:: https://github.com/fish-shell/fish-shell/workflows/make%20test/badge.svg
|
||||
:target: https://github.com/fish-shell/fish-shell/actions
|
||||
35
SECURITY.md
35
SECURITY.md
@@ -1,35 +0,0 @@
|
||||
# Security Reporting
|
||||
|
||||
If you wish to report a security vulnerability privately, we appreciate your diligence. Please follow the guidelines below to submit your report.
|
||||
|
||||
## Reporting
|
||||
|
||||
To report a security vulnerability, please provide the following information:
|
||||
|
||||
1. **PROJECT**
|
||||
|
||||
- Include the URL of the project repository - Example: <https://github.com/fish-shell/fish-shell>
|
||||
|
||||
2. **PUBLIC**
|
||||
|
||||
- Indicate whether this vulnerability has already been publicly discussed or disclosed.
|
||||
- If so, provide relevant links.
|
||||
|
||||
3. **DESCRIPTION**
|
||||
- Provide a detailed description of the security vulnerability.
|
||||
- Include as much information as possible to help us understand and address the issue.
|
||||
|
||||
Send this information, along with any additional relevant details, to <rf@fishshell.com>.
|
||||
|
||||
## Confidentiality
|
||||
|
||||
We kindly ask you to keep the report confidential until a public announcement is made.
|
||||
|
||||
## Notes
|
||||
|
||||
- Vulnerabilities will be handled on a best-effort basis.
|
||||
- You may request an advance copy of the patched release, but we cannot guarantee early access before the public release.
|
||||
- You will be notified via email simultaneously with the public announcement.
|
||||
- We will respond within a few weeks to confirm whether your report has been accepted or rejected.
|
||||
|
||||
Thank you for helping to improve the security of our project!
|
||||
105
STYLEGUIDE.md
Normal file
105
STYLEGUIDE.md
Normal file
@@ -0,0 +1,105 @@
|
||||
# Style guide
|
||||
|
||||
This is style guide for fish contributors. You should use it for any new code
|
||||
that you would add to this project and try to format existing code to use this
|
||||
style.
|
||||
|
||||
## Formatting
|
||||
|
||||
1. fish uses the Allman/BSD style of indentation.
|
||||
2. Indent with spaces, not tabs.
|
||||
3. Use 4 spaces per indent (unless needed like `Makefile`).
|
||||
4. Opening curly bracket is on the following line:
|
||||
|
||||
// ✔:
|
||||
struct name
|
||||
{
|
||||
// code
|
||||
};
|
||||
|
||||
void func()
|
||||
{
|
||||
// code
|
||||
}
|
||||
|
||||
if (...)
|
||||
{
|
||||
// code
|
||||
}
|
||||
|
||||
// ✗:
|
||||
void func() {
|
||||
// code
|
||||
}
|
||||
|
||||
5. Put space after `if`, `while` and `for` before conditions.
|
||||
|
||||
// ✔:
|
||||
if () {}
|
||||
|
||||
// ✗:
|
||||
if() {}
|
||||
|
||||
6. Put spaces before and after operators excluding increment and decrement;
|
||||
|
||||
// ✔:
|
||||
int a = 1 + 2 * 3;
|
||||
a++;
|
||||
|
||||
// ✗:
|
||||
int a=1+2*3;
|
||||
a ++;
|
||||
|
||||
7. Never put spaces between function name and parameters list.
|
||||
|
||||
// ✔:
|
||||
func(args);
|
||||
|
||||
// ✗:
|
||||
func (args);
|
||||
|
||||
8. Never put spaces after `(` and before `)`.
|
||||
9. Always put space after comma and semicolon.
|
||||
|
||||
// ✔:
|
||||
func(arg1, arg2);
|
||||
|
||||
for (int i = 0; i < LENGTH; i++) {}
|
||||
|
||||
// ✗:
|
||||
func(arg1,arg2);
|
||||
|
||||
for (int i = 0;i<LENGTH;i++) {}
|
||||
|
||||
## Documentation
|
||||
|
||||
Document your code using [Doxygen][dox].
|
||||
|
||||
1. Documentation comment should use double star notation or tripple slash:
|
||||
|
||||
// ✔:
|
||||
/// Some var
|
||||
int var;
|
||||
|
||||
/**
|
||||
* Some func
|
||||
*/
|
||||
void func();
|
||||
|
||||
2. Use slash as tag mark:
|
||||
|
||||
// ✔:
|
||||
|
||||
/**
|
||||
* \param a an integer argument.
|
||||
* \param s a constant character pointer.
|
||||
* \return The results
|
||||
*/
|
||||
int foo(int a, const char *s);
|
||||
|
||||
## Naming
|
||||
|
||||
All names in code should be `small_snake_case`. No Hungarian notation is used.
|
||||
Classes and structs names should be followed by `_t`.
|
||||
|
||||
[dox]: http://www.stack.nl/~dimitri/doxygen/ "Doxygen homepage"
|
||||
376
autoload.cpp
Normal file
376
autoload.cpp
Normal file
@@ -0,0 +1,376 @@
|
||||
/** \file autoload.cpp
|
||||
|
||||
The classes responsible for autoloading functions and completions.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "autoload.h"
|
||||
#include "wutil.h"
|
||||
#include "common.h"
|
||||
#include "signal.h"
|
||||
#include "env.h"
|
||||
#include "exec.h"
|
||||
#include <assert.h>
|
||||
#include <algorithm>
|
||||
|
||||
/* The time before we'll recheck an autoloaded file */
|
||||
static const int kAutoloadStalenessInterval = 15;
|
||||
|
||||
file_access_attempt_t access_file(const wcstring &path, int mode)
|
||||
{
|
||||
//printf("Touch %ls\n", path.c_str());
|
||||
file_access_attempt_t result = {0};
|
||||
struct stat statbuf;
|
||||
if (wstat(path, &statbuf))
|
||||
{
|
||||
result.error = errno;
|
||||
}
|
||||
else
|
||||
{
|
||||
result.mod_time = statbuf.st_mtime;
|
||||
if (waccess(path, mode))
|
||||
{
|
||||
result.error = errno;
|
||||
}
|
||||
else
|
||||
{
|
||||
result.accessible = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Note that we record the last checked time after the call, on the assumption that in a slow filesystem, the lag comes before the kernel check, not after.
|
||||
result.stale = false;
|
||||
result.last_checked = time(NULL);
|
||||
return result;
|
||||
}
|
||||
|
||||
autoload_t::autoload_t(const wcstring &env_var_name_var, const builtin_script_t * const scripts, size_t script_count) :
|
||||
lock(),
|
||||
env_var_name(env_var_name_var),
|
||||
builtin_scripts(scripts),
|
||||
builtin_script_count(script_count),
|
||||
last_path(),
|
||||
is_loading_set()
|
||||
{
|
||||
pthread_mutex_init(&lock, NULL);
|
||||
}
|
||||
|
||||
autoload_t::~autoload_t()
|
||||
{
|
||||
pthread_mutex_destroy(&lock);
|
||||
}
|
||||
|
||||
void autoload_t::node_was_evicted(autoload_function_t *node)
|
||||
{
|
||||
// This should only ever happen on the main thread
|
||||
ASSERT_IS_MAIN_THREAD();
|
||||
|
||||
// Tell ourselves that the command was removed if it was loaded
|
||||
if (! node->is_loaded)
|
||||
this->command_removed(node->key);
|
||||
delete node;
|
||||
}
|
||||
|
||||
int autoload_t::unload(const wcstring &cmd)
|
||||
{
|
||||
return this->evict_node(cmd);
|
||||
}
|
||||
|
||||
int autoload_t::load(const wcstring &cmd, bool reload)
|
||||
{
|
||||
int res;
|
||||
CHECK_BLOCK(0);
|
||||
ASSERT_IS_MAIN_THREAD();
|
||||
|
||||
env_var_t path_var = env_get_string(env_var_name);
|
||||
|
||||
/*
|
||||
Do we know where to look?
|
||||
*/
|
||||
if (path_var.empty())
|
||||
return 0;
|
||||
|
||||
/* Check if the lookup path has changed. If so, drop all loaded files. path_var may only be inspected on the main thread. */
|
||||
if (path_var != this->last_path)
|
||||
{
|
||||
this->last_path = path_var;
|
||||
scoped_lock locker(lock);
|
||||
this->evict_all_nodes();
|
||||
}
|
||||
|
||||
/** Warn and fail on infinite recursion. It's OK to do this because this function is only called on the main thread. */
|
||||
if (this->is_loading(cmd))
|
||||
{
|
||||
debug(0,
|
||||
_(L"Could not autoload item '%ls', it is already being autoloaded. "
|
||||
L"This is a circular dependency in the autoloading scripts, please remove it."),
|
||||
cmd.c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Mark that we're loading this */
|
||||
is_loading_set.insert(cmd);
|
||||
|
||||
/* Get the list of paths from which we will try to load */
|
||||
std::vector<wcstring> path_list;
|
||||
tokenize_variable_array(path_var, path_list);
|
||||
|
||||
/* Try loading it */
|
||||
res = this->locate_file_and_maybe_load_it(cmd, true, reload, path_list);
|
||||
|
||||
/* Clean up */
|
||||
bool erased = !! is_loading_set.erase(cmd);
|
||||
assert(erased);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool autoload_t::can_load(const wcstring &cmd, const env_vars_snapshot_t &vars)
|
||||
{
|
||||
const env_var_t path_var = vars.get(env_var_name);
|
||||
if (path_var.missing_or_empty())
|
||||
return false;
|
||||
|
||||
std::vector<wcstring> path_list;
|
||||
tokenize_variable_array(path_var, path_list);
|
||||
return this->locate_file_and_maybe_load_it(cmd, false, false, path_list);
|
||||
}
|
||||
|
||||
static bool script_name_precedes_script_name(const builtin_script_t &script1, const builtin_script_t &script2)
|
||||
{
|
||||
return wcscmp(script1.name, script2.name) < 0;
|
||||
}
|
||||
|
||||
void autoload_t::unload_all(void)
|
||||
{
|
||||
scoped_lock locker(lock);
|
||||
this->evict_all_nodes();
|
||||
}
|
||||
|
||||
/** Check whether the given command is loaded. */
|
||||
bool autoload_t::has_tried_loading(const wcstring &cmd)
|
||||
{
|
||||
scoped_lock locker(lock);
|
||||
autoload_function_t * func = this->get_node(cmd);
|
||||
return func != NULL;
|
||||
}
|
||||
|
||||
static bool is_stale(const autoload_function_t *func)
|
||||
{
|
||||
/** Return whether this function is stale. Internalized functions can never be stale. */
|
||||
return ! func->is_internalized && time(NULL) - func->access.last_checked > kAutoloadStalenessInterval;
|
||||
}
|
||||
|
||||
autoload_function_t *autoload_t::get_autoloaded_function_with_creation(const wcstring &cmd, bool allow_eviction)
|
||||
{
|
||||
ASSERT_IS_LOCKED(lock);
|
||||
autoload_function_t *func = this->get_node(cmd);
|
||||
if (! func)
|
||||
{
|
||||
func = new autoload_function_t(cmd);
|
||||
if (allow_eviction)
|
||||
{
|
||||
this->add_node(func);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->add_node_without_eviction(func);
|
||||
}
|
||||
}
|
||||
return func;
|
||||
}
|
||||
|
||||
/**
|
||||
This internal helper function does all the real work. By using two
|
||||
functions, the internal function can return on various places in
|
||||
the code, and the caller can take care of various cleanup work.
|
||||
|
||||
cmd: the command name ('grep')
|
||||
really_load: whether to actually parse it as a function, or just check it it exists
|
||||
reload: whether to reload it if it's already loaded
|
||||
path_list: the set of paths to check
|
||||
|
||||
Result: if really_load is true, returns whether the function was loaded. Otherwise returns whether the function existed.
|
||||
*/
|
||||
bool autoload_t::locate_file_and_maybe_load_it(const wcstring &cmd, bool really_load, bool reload, const wcstring_list_t &path_list)
|
||||
{
|
||||
/* Note that we are NOT locked in this function! */
|
||||
size_t i;
|
||||
bool reloaded = 0;
|
||||
|
||||
/* Try using a cached function. If we really want the function to be loaded, require that it be really loaded. If we're not reloading, allow stale functions. */
|
||||
{
|
||||
bool allow_stale_functions = ! reload;
|
||||
|
||||
/* Take a lock */
|
||||
scoped_lock locker(lock);
|
||||
|
||||
/* Get the function */
|
||||
autoload_function_t * func = this->get_node(cmd);
|
||||
|
||||
/* Determine if we can use this cached function */
|
||||
bool use_cached;
|
||||
if (! func)
|
||||
{
|
||||
/* Can't use a function that doesn't exist */
|
||||
use_cached = false;
|
||||
}
|
||||
else if (really_load && ! func->is_placeholder && ! func->is_loaded)
|
||||
{
|
||||
/* Can't use an unloaded function */
|
||||
use_cached = false;
|
||||
}
|
||||
else if (! allow_stale_functions && is_stale(func))
|
||||
{
|
||||
/* Can't use a stale function */
|
||||
use_cached = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* I guess we can use it */
|
||||
use_cached = true;
|
||||
}
|
||||
|
||||
/* If we can use this function, return whether we were able to access it */
|
||||
if (use_cached)
|
||||
{
|
||||
return func->is_internalized || func->access.accessible;
|
||||
}
|
||||
}
|
||||
/* The source of the script will end up here */
|
||||
wcstring script_source;
|
||||
bool has_script_source = false;
|
||||
|
||||
/* Whether we found an accessible file */
|
||||
bool found_file = false;
|
||||
|
||||
/* Look for built-in scripts via a binary search */
|
||||
const builtin_script_t *matching_builtin_script = NULL;
|
||||
if (builtin_script_count > 0)
|
||||
{
|
||||
const builtin_script_t test_script = {cmd.c_str(), NULL};
|
||||
const builtin_script_t *array_end = builtin_scripts + builtin_script_count;
|
||||
const builtin_script_t *found = std::lower_bound(builtin_scripts, array_end, test_script, script_name_precedes_script_name);
|
||||
if (found != array_end && ! wcscmp(found->name, test_script.name))
|
||||
{
|
||||
/* We found it */
|
||||
matching_builtin_script = found;
|
||||
}
|
||||
}
|
||||
if (matching_builtin_script)
|
||||
{
|
||||
has_script_source = true;
|
||||
script_source = str2wcstring(matching_builtin_script->def);
|
||||
|
||||
/* Make a node representing this function */
|
||||
scoped_lock locker(lock);
|
||||
autoload_function_t *func = this->get_autoloaded_function_with_creation(cmd, really_load);
|
||||
|
||||
/* This function is internalized */
|
||||
func->is_internalized = true;
|
||||
|
||||
/* It's a fiction to say the script is loaded at this point, but we're definitely going to load it down below. */
|
||||
if (really_load) func->is_loaded = true;
|
||||
}
|
||||
|
||||
if (! has_script_source)
|
||||
{
|
||||
/* Iterate over path searching for suitable completion files */
|
||||
for (i=0; i<path_list.size(); i++)
|
||||
{
|
||||
wcstring next = path_list.at(i);
|
||||
wcstring path = next + L"/" + cmd + L".fish";
|
||||
|
||||
const file_access_attempt_t access = access_file(path, R_OK);
|
||||
if (access.accessible)
|
||||
{
|
||||
/* Found it! */
|
||||
found_file = true;
|
||||
|
||||
/* Now we're actually going to take the lock. */
|
||||
scoped_lock locker(lock);
|
||||
autoload_function_t *func = this->get_node(cmd);
|
||||
|
||||
/* Generate the source if we need to load it */
|
||||
bool need_to_load_function = really_load && (func == NULL || func->access.mod_time != access.mod_time || ! func->is_loaded);
|
||||
if (need_to_load_function)
|
||||
{
|
||||
|
||||
/* Generate the script source */
|
||||
wcstring esc = escape_string(path, 1);
|
||||
script_source = L". " + esc;
|
||||
has_script_source = true;
|
||||
|
||||
/* Remove any loaded command because we are going to reload it. Note that this will deadlock if command_removed calls back into us. */
|
||||
if (func && func->is_loaded)
|
||||
{
|
||||
command_removed(cmd);
|
||||
func->is_placeholder = false;
|
||||
}
|
||||
|
||||
/* Mark that we're reloading it */
|
||||
reloaded = true;
|
||||
}
|
||||
|
||||
/* Create the function if we haven't yet. This does not load it. Do not trigger eviction unless we are actually loading, because we don't want to evict off of the main thread. */
|
||||
if (! func)
|
||||
{
|
||||
func = get_autoloaded_function_with_creation(cmd, really_load);
|
||||
}
|
||||
|
||||
/* It's a fiction to say the script is loaded at this point, but we're definitely going to load it down below. */
|
||||
if (need_to_load_function) func->is_loaded = true;
|
||||
|
||||
/* Unconditionally record our access time */
|
||||
func->access = access;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
If no file or builtin script was found we insert a placeholder function.
|
||||
Later we only research if the current time is at least five seconds later.
|
||||
This way, the files won't be searched over and over again.
|
||||
*/
|
||||
if (! found_file && ! has_script_source)
|
||||
{
|
||||
scoped_lock locker(lock);
|
||||
/* Generate a placeholder */
|
||||
autoload_function_t *func = this->get_node(cmd);
|
||||
if (! func)
|
||||
{
|
||||
func = new autoload_function_t(cmd);
|
||||
func->is_placeholder = true;
|
||||
if (really_load)
|
||||
{
|
||||
this->add_node(func);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->add_node_without_eviction(func);
|
||||
}
|
||||
}
|
||||
func->access.last_checked = time(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/* If we have a script, either built-in or a file source, then run it */
|
||||
if (really_load && has_script_source)
|
||||
{
|
||||
if (exec_subshell(script_source, false /* do not apply exit status */) == -1)
|
||||
{
|
||||
/* Do nothing on failure */
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (really_load)
|
||||
{
|
||||
return reloaded;
|
||||
}
|
||||
else
|
||||
{
|
||||
return found_file || has_script_source;
|
||||
}
|
||||
}
|
||||
138
autoload.h
Normal file
138
autoload.h
Normal file
@@ -0,0 +1,138 @@
|
||||
/** \file autoload.h
|
||||
|
||||
The classes responsible for autoloading functions and completions.
|
||||
*/
|
||||
|
||||
#ifndef FISH_AUTOLOAD_H
|
||||
#define FISH_AUTOLOAD_H
|
||||
|
||||
#include <wchar.h>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <list>
|
||||
#include "common.h"
|
||||
#include "lru.h"
|
||||
|
||||
/** A struct responsible for recording an attempt to access a file. */
|
||||
struct file_access_attempt_t
|
||||
{
|
||||
time_t mod_time; /** The modification time of the file */
|
||||
time_t last_checked; /** When we last checked the file */
|
||||
bool accessible; /** Whether we believe we could access this file */
|
||||
bool stale; /** Whether the access attempt is stale */
|
||||
int error; /** If we could not access the file, the error code */
|
||||
};
|
||||
|
||||
file_access_attempt_t access_file(const wcstring &path, int mode);
|
||||
|
||||
struct autoload_function_t : public lru_node_t
|
||||
{
|
||||
autoload_function_t(const wcstring &key) : lru_node_t(key), access(), is_loaded(false), is_placeholder(false), is_internalized(false) { }
|
||||
file_access_attempt_t access; /** The last access attempt */
|
||||
bool is_loaded; /** Whether we have actually loaded this function */
|
||||
bool is_placeholder; /** Whether we are a placeholder that stands in for "no such function". If this is true, then is_loaded must be false. */
|
||||
bool is_internalized; /** Whether this function came from a builtin "internalized" script */
|
||||
};
|
||||
|
||||
struct builtin_script_t
|
||||
{
|
||||
const wchar_t *name;
|
||||
const char *def;
|
||||
};
|
||||
|
||||
struct builtin_script_t;
|
||||
class env_vars_snapshot_t;
|
||||
|
||||
/**
|
||||
A class that represents a path from which we can autoload, and the autoloaded contents.
|
||||
*/
|
||||
class autoload_t : private lru_cache_t<autoload_function_t>
|
||||
{
|
||||
private:
|
||||
|
||||
/** Lock for thread safety */
|
||||
pthread_mutex_t lock;
|
||||
|
||||
/** The environment variable name */
|
||||
const wcstring env_var_name;
|
||||
|
||||
/** Builtin script array */
|
||||
const struct builtin_script_t *const builtin_scripts;
|
||||
|
||||
/** Builtin script count */
|
||||
const size_t builtin_script_count;
|
||||
|
||||
/** The path from which we most recently autoloaded */
|
||||
wcstring last_path;
|
||||
|
||||
/**
|
||||
A table containing all the files that are currently being
|
||||
loaded. This is here to help prevent recursion.
|
||||
*/
|
||||
std::set<wcstring> is_loading_set;
|
||||
|
||||
bool is_loading(const wcstring &name) const
|
||||
{
|
||||
return is_loading_set.find(name) != is_loading_set.end();
|
||||
}
|
||||
|
||||
void remove_all_functions(void)
|
||||
{
|
||||
this->evict_all_nodes();
|
||||
}
|
||||
|
||||
bool locate_file_and_maybe_load_it(const wcstring &cmd, bool really_load, bool reload, const wcstring_list_t &path_list);
|
||||
|
||||
virtual void node_was_evicted(autoload_function_t *node);
|
||||
|
||||
autoload_function_t *get_autoloaded_function_with_creation(const wcstring &cmd, bool allow_eviction);
|
||||
|
||||
protected:
|
||||
/** Overridable callback for when a command is removed */
|
||||
virtual void command_removed(const wcstring &cmd) { }
|
||||
|
||||
public:
|
||||
|
||||
/** Create an autoload_t for the given environment variable name */
|
||||
autoload_t(const wcstring &env_var_name_var, const builtin_script_t *scripts, size_t script_count);
|
||||
|
||||
/** Destructor */
|
||||
virtual ~autoload_t();
|
||||
|
||||
/**
|
||||
Autoload the specified file, if it exists in the specified path. Do
|
||||
not load it multiple times unless its timestamp changes or
|
||||
parse_util_unload is called.
|
||||
|
||||
Autoloading one file may unload another.
|
||||
|
||||
\param cmd the filename to search for. The suffix '.fish' is always added to this name
|
||||
\param on_unload a callback function to run if a suitable file is found, which has not already been run. unload will also be called for old files which are unloaded.
|
||||
\param reload wheter to recheck file timestamps on already loaded files
|
||||
*/
|
||||
int load(const wcstring &cmd, bool reload);
|
||||
|
||||
/** Check whether we have tried loading the given command. Does not do any I/O. */
|
||||
bool has_tried_loading(const wcstring &cmd);
|
||||
|
||||
/**
|
||||
Tell the autoloader that the specified file, in the specified path,
|
||||
is no longer loaded.
|
||||
|
||||
\param cmd the filename to search for. The suffix '.fish' is always added to this name
|
||||
\param on_unload a callback function which will be called before (re)loading a file, may be used to unload the previous file.
|
||||
\return non-zero if the file was removed, zero if the file had not yet been loaded
|
||||
*/
|
||||
int unload(const wcstring &cmd);
|
||||
|
||||
/**
|
||||
Unloads all files.
|
||||
*/
|
||||
void unload_all();
|
||||
|
||||
/** Check whether the given command could be loaded, but do not load it. */
|
||||
bool can_load(const wcstring &cmd, const env_vars_snapshot_t &vars);
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,3 +0,0 @@
|
||||
for i in (seq 1000)
|
||||
command true
|
||||
end
|
||||
@@ -1,7 +0,0 @@
|
||||
# Glob fish's source directory.
|
||||
# This timing is bound to change if the repo does,
|
||||
# so it's best to build two fishes, check out one version of the repo,
|
||||
# and then run this script with both.
|
||||
set -l dir (dirname (status current-filename))
|
||||
# No repetitions, this is plenty slow enough.
|
||||
echo $dir/../../**
|
||||
@@ -1,11 +0,0 @@
|
||||
set -l compdir (status dirname)/../../share/completions
|
||||
cd $compdir
|
||||
for file in *.fish
|
||||
set -l bname (string replace -r '.fish$' '' -- $file)
|
||||
if type -q $bname
|
||||
source $file >/dev/null
|
||||
if test $status -gt 0
|
||||
echo FAILING FILE $file
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,3 +0,0 @@
|
||||
for i in (seq 100000)
|
||||
math $i + $i
|
||||
end
|
||||
@@ -1,6 +0,0 @@
|
||||
set -l path (status dirname)
|
||||
set -l fish (status fish-path)
|
||||
for f in (seq 100)
|
||||
echo $fish -n $path/aliases.fish
|
||||
$fish -n $path/aliases.fish
|
||||
end
|
||||
@@ -1 +0,0 @@
|
||||
printf (string repeat -n 200 \\x7f)%s\n (string repeat -n 2000 aaa\n)
|
||||
@@ -1,5 +0,0 @@
|
||||
for i in (seq 100000)
|
||||
printf '%f\n' $i.$i
|
||||
end
|
||||
|
||||
exit 0
|
||||
@@ -1,7 +0,0 @@
|
||||
set -l tmp (mktemp)
|
||||
string repeat -n 2000 >$tmp
|
||||
for i in (seq 1000)
|
||||
cat $tmp | read -l foo
|
||||
end
|
||||
|
||||
true
|
||||
@@ -1,3 +0,0 @@
|
||||
for i in (seq 10000)
|
||||
echo $i
|
||||
end
|
||||
@@ -1,3 +0,0 @@
|
||||
for abc in (seq 100000)
|
||||
set -l def
|
||||
end
|
||||
@@ -1,3 +0,0 @@
|
||||
for i in (string repeat -n 100 \n)
|
||||
string repeat -n 50000 a\n
|
||||
end
|
||||
@@ -1,3 +0,0 @@
|
||||
for i in (seq 100000)
|
||||
string match '*o' fooooooo
|
||||
end
|
||||
@@ -1,3 +0,0 @@
|
||||
for i in (seq 100000)
|
||||
string match -r '^.*$' fooooooo
|
||||
end | string match -re o
|
||||
@@ -1,43 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
if [ "$#" -gt 2 ] || [ "$#" -eq 0 ]; then
|
||||
echo "Usage: driver.sh /path/to/fish [/path/to/other/fish]"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
FISH_PATH=$1
|
||||
FISH2_PATH=$2
|
||||
BENCHMARKS_DIR=$(dirname "$0")/benchmarks
|
||||
|
||||
quote() {
|
||||
# Single-quote the given string for a POSIX shell, except in common cases that don't need it.
|
||||
printf %s "$1" |
|
||||
sed "/[^[:alnum:]\/.-]/ {
|
||||
s/'/'\\\''/g
|
||||
s/^/'/
|
||||
s/\$/'/
|
||||
}"
|
||||
}
|
||||
|
||||
for benchmark in "$BENCHMARKS_DIR"/*; do
|
||||
basename "$benchmark"
|
||||
# If we have hyperfine, use it first to warm up the cache
|
||||
if command -v hyperfine >/dev/null 2>&1; then
|
||||
cmd1="$(quote "${FISH_PATH}") --no-config $(quote "$benchmark")"
|
||||
if [ -n "$FISH2_PATH" ]; then
|
||||
cmd2="$(quote "${FISH2_PATH}") --no-config $(quote "$benchmark")"
|
||||
hyperfine --warmup 3 "$cmd1" "$cmd2"
|
||||
else
|
||||
hyperfine --warmup 3 "$cmd1"
|
||||
fi
|
||||
fi
|
||||
|
||||
[ -n "$FISH2_PATH" ] && echo "$FISH_PATH"
|
||||
"${FISH_PATH}" --print-rusage-self "$benchmark" > /dev/null
|
||||
if [ -n "$FISH2_PATH" ]; then
|
||||
echo "$FISH2_PATH"
|
||||
"${FISH2_PATH}" --print-rusage-self "$benchmark" > /dev/null
|
||||
fi
|
||||
|
||||
done
|
||||
|
||||
309
build.rs
309
build.rs
@@ -1,309 +0,0 @@
|
||||
#![allow(clippy::uninlined_format_args)]
|
||||
|
||||
use fish_build_helper::{fish_build_dir, workspace_root};
|
||||
use rsconf::Target;
|
||||
use std::env;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
fn canonicalize<P: AsRef<Path>>(path: P) -> PathBuf {
|
||||
std::fs::canonicalize(path).unwrap()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
setup_paths();
|
||||
|
||||
// Add our default to enable tools that don't go through CMake, like "cargo test" and the
|
||||
// language server.
|
||||
|
||||
rsconf::set_env_value(
|
||||
"FISH_RESOLVED_BUILD_DIR",
|
||||
// If set by CMake, this might include symlinks. Since we want to compare this to the
|
||||
// dir fish is executed in we need to canonicalize it.
|
||||
canonicalize(fish_build_dir()).to_str().unwrap(),
|
||||
);
|
||||
|
||||
// We need to canonicalize (i.e. realpath) the manifest dir because we want to be able to
|
||||
// compare it directly as a string at runtime.
|
||||
rsconf::set_env_value(
|
||||
"CARGO_MANIFEST_DIR",
|
||||
canonicalize(workspace_root()).to_str().unwrap(),
|
||||
);
|
||||
|
||||
// Some build info
|
||||
rsconf::set_env_value("BUILD_TARGET_TRIPLE", &env::var("TARGET").unwrap());
|
||||
rsconf::set_env_value("BUILD_HOST_TRIPLE", &env::var("HOST").unwrap());
|
||||
rsconf::set_env_value("BUILD_PROFILE", &env::var("PROFILE").unwrap());
|
||||
|
||||
let version = &get_version(&env::current_dir().unwrap());
|
||||
// Per https://doc.rust-lang.org/cargo/reference/build-scripts.html#inputs-to-the-build-script,
|
||||
// the source directory is the current working directory of the build script
|
||||
rsconf::set_env_value("FISH_BUILD_VERSION", version);
|
||||
|
||||
std::env::set_var("FISH_BUILD_VERSION", version);
|
||||
|
||||
// These are necessary if built with embedded functions,
|
||||
// but only in release builds (because rust-embed in debug builds reads from the filesystem).
|
||||
#[cfg(feature = "embed-data")]
|
||||
#[cfg(any(windows, not(debug_assertions)))]
|
||||
rsconf::rebuild_if_path_changed("share");
|
||||
|
||||
#[cfg(feature = "gettext-extract")]
|
||||
rsconf::rebuild_if_env_changed("FISH_GETTEXT_EXTRACTION_FILE");
|
||||
|
||||
rsconf::rebuild_if_path_changed("src/libc.c");
|
||||
cc::Build::new().file("src/libc.c").compile("flibc.a");
|
||||
|
||||
let build = cc::Build::new();
|
||||
let mut target = Target::new_from(build).unwrap();
|
||||
// Keep verbose mode on until we've ironed out rust build script stuff
|
||||
target.set_verbose(true);
|
||||
detect_cfgs(&mut target);
|
||||
|
||||
#[cfg(all(target_env = "gnu", target_feature = "crt-static"))]
|
||||
compile_error!("Statically linking against glibc has unavoidable crashes and is unsupported. Use dynamic linking or link statically against musl.");
|
||||
}
|
||||
|
||||
/// Check target system support for certain functionality dynamically when the build is invoked,
|
||||
/// without their having to be explicitly enabled in the `cargo build --features xxx` invocation.
|
||||
///
|
||||
/// We are using [`rsconf::enable_cfg()`] instead of [`rsconf::enable_feature()`] as rust features
|
||||
/// should be used for things that a user can/would reasonably enable or disable to tweak or coerce
|
||||
/// behavior, but here we are testing for whether or not things are supported altogether.
|
||||
///
|
||||
/// This can be used to enable features that we check for and conditionally compile according to in
|
||||
/// our own codebase, but [can't be used to pull in dependencies](0) even if they're gated (in
|
||||
/// `Cargo.toml`) behind a feature we just enabled.
|
||||
///
|
||||
/// [0]: https://github.com/rust-lang/cargo/issues/5499
|
||||
#[rustfmt::skip]
|
||||
fn detect_cfgs(target: &mut Target) {
|
||||
for (name, handler) in [
|
||||
// Ignore the first entry, it just sets up the type inference. Model new entries after the
|
||||
// second line.
|
||||
("", &(|_: &Target| false) as &dyn Fn(&Target) -> bool),
|
||||
("apple", &detect_apple),
|
||||
("bsd", &detect_bsd),
|
||||
("cygwin", &detect_cygwin),
|
||||
("small_main_stack", &has_small_stack),
|
||||
// See if libc supports the thread-safe localeconv_l(3) alternative to localeconv(3).
|
||||
("localeconv_l", &|target| {
|
||||
target.has_symbol("localeconv_l")
|
||||
}),
|
||||
("FISH_USE_POSIX_SPAWN", &|target| {
|
||||
target.has_header("spawn.h")
|
||||
}),
|
||||
("HAVE_PIPE2", &|target| {
|
||||
target.has_symbol("pipe2")
|
||||
}),
|
||||
("HAVE_EVENTFD", &|target| {
|
||||
// FIXME: NetBSD 10 has eventfd, but the libc crate does not expose it.
|
||||
if cfg!(target_os = "netbsd") {
|
||||
false
|
||||
} else {
|
||||
target.has_header("sys/eventfd.h")
|
||||
}
|
||||
}),
|
||||
("HAVE_WAITSTATUS_SIGNAL_RET", &|target| {
|
||||
target.r#if("WEXITSTATUS(0x007f) == 0x7f", &["sys/wait.h"])
|
||||
}),
|
||||
] {
|
||||
rsconf::declare_cfg(name, handler(target))
|
||||
}
|
||||
}
|
||||
|
||||
fn detect_apple(_: &Target) -> bool {
|
||||
cfg!(any(target_os = "ios", target_os = "macos"))
|
||||
}
|
||||
|
||||
fn detect_cygwin(_: &Target) -> bool {
|
||||
// Cygwin target is usually cross-compiled.
|
||||
std::env::var("CARGO_CFG_TARGET_OS").unwrap() == "cygwin"
|
||||
}
|
||||
|
||||
/// Detect if we're being compiled for a BSD-derived OS, allowing targeting code conditionally with
|
||||
/// `#[cfg(bsd)]`.
|
||||
///
|
||||
/// Rust offers fine-grained conditional compilation per-os for the popular operating systems, but
|
||||
/// doesn't necessarily include less-popular forks nor does it group them into families more
|
||||
/// specific than "windows" vs "unix" so we can conditionally compile code for BSD systems.
|
||||
fn detect_bsd(_: &Target) -> bool {
|
||||
// Instead of using `uname`, we can inspect the TARGET env variable set by Cargo. This lets us
|
||||
// support cross-compilation scenarios.
|
||||
let mut target = std::env::var("TARGET").unwrap();
|
||||
if !target.chars().all(|c| c.is_ascii_lowercase()) {
|
||||
target = target.to_ascii_lowercase();
|
||||
}
|
||||
#[allow(clippy::let_and_return)] // for old clippy
|
||||
let is_bsd = target.ends_with("bsd") || target.ends_with("dragonfly");
|
||||
#[cfg(any(
|
||||
target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd",
|
||||
))]
|
||||
assert!(is_bsd, "Target incorrectly detected as not BSD!");
|
||||
is_bsd
|
||||
}
|
||||
|
||||
/// Rust sets the stack size of newly created threads to a sane value, but is at at the mercy of the
|
||||
/// OS when it comes to the size of the main stack. Some platforms we support default to a tiny
|
||||
/// 0.5 MiB main stack, which is insufficient for fish's MAX_EVAL_DEPTH/MAX_STACK_DEPTH values.
|
||||
///
|
||||
/// 0.5 MiB is small enough that we'd have to drastically reduce MAX_STACK_DEPTH to less than 10, so
|
||||
/// we instead use a workaround to increase the main thread size.
|
||||
fn has_small_stack(_: &Target) -> bool {
|
||||
#[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "netbsd")))]
|
||||
return false;
|
||||
|
||||
// NetBSD 10 also needs this but can't find pthread_get_stacksize_np.
|
||||
#[cfg(target_os = "netbsd")]
|
||||
return true;
|
||||
|
||||
#[cfg(any(target_os = "ios", target_os = "macos"))]
|
||||
{
|
||||
use core::ffi;
|
||||
|
||||
extern "C" {
|
||||
fn pthread_get_stacksize_np(thread: *const ffi::c_void) -> usize;
|
||||
fn pthread_self() -> *const ffi::c_void;
|
||||
}
|
||||
|
||||
// build.rs is executed on the main thread, so we are getting the main thread's stack size.
|
||||
// Modern macOS versions default to an 8 MiB main stack but legacy OS X have a 0.5 MiB one.
|
||||
let stack_size = unsafe { pthread_get_stacksize_np(pthread_self()) };
|
||||
const TWO_MIB: usize = 2 * 1024 * 1024 - 1;
|
||||
match stack_size {
|
||||
0..=TWO_MIB => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn setup_paths() {
|
||||
#[cfg(windows)]
|
||||
use unix_path::{Path, PathBuf};
|
||||
|
||||
fn get_path(name: &str, default: &str, onvar: &Path) -> PathBuf {
|
||||
let mut var = PathBuf::from(env::var(name).unwrap_or(default.to_string()));
|
||||
if var.is_relative() {
|
||||
var = onvar.join(var);
|
||||
}
|
||||
var
|
||||
}
|
||||
|
||||
let prefix = PathBuf::from(env::var("PREFIX").unwrap_or("/usr/local".to_string()));
|
||||
rsconf::rebuild_if_env_changed("PREFIX");
|
||||
rsconf::set_env_value("PREFIX", prefix.to_str().unwrap());
|
||||
|
||||
let datadir = get_path("DATADIR", "share/", &prefix);
|
||||
|
||||
let sysconfdir = get_path(
|
||||
"SYSCONFDIR",
|
||||
// Embedded builds use "/etc," not "./share/etc".
|
||||
if cfg!(feature = "embed-data") {
|
||||
"/etc/"
|
||||
} else {
|
||||
"etc/"
|
||||
},
|
||||
&datadir,
|
||||
);
|
||||
rsconf::set_env_value("SYSCONFDIR", sysconfdir.to_str().unwrap());
|
||||
rsconf::rebuild_if_env_changed("SYSCONFDIR");
|
||||
|
||||
#[cfg(not(feature = "embed-data"))]
|
||||
{
|
||||
rsconf::set_env_value("DATADIR", datadir.to_str().unwrap());
|
||||
rsconf::rebuild_if_env_changed("DATADIR");
|
||||
|
||||
let bindir = get_path("BINDIR", "bin/", &prefix);
|
||||
rsconf::set_env_value("BINDIR", bindir.to_str().unwrap());
|
||||
rsconf::rebuild_if_env_changed("BINDIR");
|
||||
|
||||
let localedir = get_path("LOCALEDIR", "locale/", &datadir);
|
||||
let localedir = localedir.to_str().unwrap();
|
||||
assert!(!localedir.is_empty(), "empty LOCALEDIR is not supported");
|
||||
rsconf::set_env_value("LOCALEDIR", localedir);
|
||||
rsconf::rebuild_if_env_changed("LOCALEDIR");
|
||||
|
||||
let docdir = get_path("DOCDIR", "doc/fish", &datadir);
|
||||
rsconf::set_env_value("DOCDIR", docdir.to_str().unwrap());
|
||||
rsconf::rebuild_if_env_changed("DOCDIR");
|
||||
}
|
||||
}
|
||||
|
||||
fn get_version(src_dir: &Path) -> String {
|
||||
use std::fs::read_to_string;
|
||||
use std::process::Command;
|
||||
|
||||
if let Ok(var) = std::env::var("FISH_BUILD_VERSION") {
|
||||
return var;
|
||||
}
|
||||
|
||||
let path = src_dir.join("version");
|
||||
if let Ok(strver) = read_to_string(path) {
|
||||
return strver;
|
||||
}
|
||||
|
||||
let args = &["describe", "--always", "--dirty=-dirty"];
|
||||
if let Ok(output) = Command::new("git").args(args).output() {
|
||||
let rev = String::from_utf8_lossy(&output.stdout).trim().to_string();
|
||||
if !rev.is_empty() {
|
||||
// If it contains a ".", we have a proper version like "3.7",
|
||||
// or "23.2.1-1234-gfab1234"
|
||||
if rev.contains('.') {
|
||||
return rev;
|
||||
}
|
||||
// If it doesn't, we probably got *just* the commit SHA,
|
||||
// like "f1242abcdef".
|
||||
// So we prepend the crate version so it at least looks like
|
||||
// "3.8-gf1242abcdef"
|
||||
// This lacks the commit *distance*, but that can't be helped without
|
||||
// tags.
|
||||
let version = env!("CARGO_PKG_VERSION").to_owned();
|
||||
return version + "-g" + &rev;
|
||||
}
|
||||
}
|
||||
|
||||
// git did not tell us a SHA either because it isn't installed,
|
||||
// or because it refused (safe.directory applies to `git describe`!)
|
||||
// So we read the SHA ourselves.
|
||||
fn get_git_hash() -> Result<String, Box<dyn std::error::Error>> {
|
||||
let workspace_root = workspace_root();
|
||||
let gitdir = workspace_root.join(".git");
|
||||
let jjdir = workspace_root.join(".jj");
|
||||
let commit_id = if gitdir.exists() {
|
||||
// .git/HEAD contains ref: refs/heads/branch
|
||||
let headpath = gitdir.join("HEAD");
|
||||
let headstr = read_to_string(headpath)?;
|
||||
let headref = headstr.split(' ').nth(1).unwrap().trim();
|
||||
|
||||
// .git/refs/heads/branch contains the SHA
|
||||
let refpath = gitdir.join(headref);
|
||||
// Shorten to 9 characters (what git describe does currently)
|
||||
read_to_string(refpath)?
|
||||
} else if jjdir.exists() {
|
||||
let output = Command::new("jj")
|
||||
.args([
|
||||
"log",
|
||||
"--revisions",
|
||||
"@",
|
||||
"--no-graph",
|
||||
"--ignore-working-copy",
|
||||
"--template",
|
||||
"commit_id",
|
||||
])
|
||||
.output()
|
||||
.unwrap();
|
||||
String::from_utf8_lossy(&output.stdout).to_string()
|
||||
} else {
|
||||
return Err("did not find either of .git or .jj".into());
|
||||
};
|
||||
let refstr = &commit_id[0..9];
|
||||
let refstr = refstr.trim();
|
||||
|
||||
let version = env!("CARGO_PKG_VERSION").to_owned();
|
||||
Ok(version + "-g" + refstr)
|
||||
}
|
||||
|
||||
get_git_hash().expect("Could not get a version. Either set $FISH_BUILD_VERSION or install git.")
|
||||
}
|
||||
136
build_tools/build_documentation.sh
Executable file
136
build_tools/build_documentation.sh
Executable file
@@ -0,0 +1,136 @@
|
||||
#!/bin/sh
|
||||
|
||||
# This script is run as part of the build process
|
||||
|
||||
if test $# -eq 0
|
||||
then
|
||||
# Use fish's defaults
|
||||
DOXYFILE=Doxyfile.help
|
||||
INPUTDIR=doc_src
|
||||
OUTPUTDIR=share
|
||||
echo "Using defaults: $0 ${DOXYFILE} ${INPUTDIR} ${OUTPUTDIR}"
|
||||
elif test $# -eq 3
|
||||
then
|
||||
DOXYFILE="$1"
|
||||
INPUTDIR="$2"
|
||||
OUTPUTDIR="$3"
|
||||
else
|
||||
echo "Usage: $0 doxygen_file input_directory output_directory"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Determine which man pages we don't want to generate.
|
||||
# Don't make a test man page. fish's test is conforming, so the system man pages
|
||||
# are applicable and generally better.
|
||||
# on OS X, don't make a man page for open, since we defeat fish's open function on OS X.
|
||||
CONDEMNED_PAGES=test.1
|
||||
if test `uname` = 'Darwin'; then
|
||||
CONDEMNED_PAGES="$CONDEMNED_PAGES open.1"
|
||||
fi
|
||||
|
||||
# Helper function to turn a relative path into an absolute path
|
||||
resolve_path()
|
||||
{
|
||||
D=`command dirname "$1"`
|
||||
B=`command basename "$1"`
|
||||
echo "`cd \"$D\" 2>/dev/null && pwd || echo \"$D\"`/$B"
|
||||
}
|
||||
|
||||
# Expand relative paths
|
||||
DOXYFILE=`resolve_path "$DOXYFILE"`
|
||||
INPUTDIR=`resolve_path "$INPUTDIR"`
|
||||
OUTPUTDIR=`resolve_path "$OUTPUTDIR"`
|
||||
|
||||
echo " doxygen file: $DOXYFILE"
|
||||
echo " input directory: $INPUTDIR"
|
||||
echo " output directory: $OUTPUTDIR"
|
||||
echo " skipping: $CONDEMNED_PAGES"
|
||||
|
||||
# Make sure INPUTDIR is found
|
||||
if test ! -d "$INPUTDIR"; then
|
||||
echo >&2 "Could not find input directory '${INPUTDIR}'"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Make sure doxygen is found
|
||||
DOXYGENPATH=`command -v doxygen`
|
||||
if test -z "$DOXYGENPATH" ; then
|
||||
for i in /usr/local/bin/doxygen /opt/bin/doxygen /Applications/Doxygen.app/Contents/Resources/doxygen ~/Applications/Doxygen.app/Contents/Resources/doxygen ; do
|
||||
if test -f "$i"; then
|
||||
DOXYGENPATH="$i"
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
if test -z "$DOXYGENPATH"; then
|
||||
echo >&2 "doxygen is not installed, so documentation will not be built."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Determine where our output should go
|
||||
if ! mkdir -p "${OUTPUTDIR}" ; then
|
||||
echo "Could not create output directory '${OUTPUTDIR}'"
|
||||
fi
|
||||
|
||||
# Make a temporary directory
|
||||
TMPLOC=`mktemp -d -t fish_doc_build_XXXXXX` || { echo >&2 "Could not build documentation because mktemp failed"; exit 1; }
|
||||
|
||||
# Copy stuff to the temp directory
|
||||
for i in "$INPUTDIR"/*.txt; do
|
||||
INPUTFILE=$TMPLOC/`basename $i .txt`.doxygen
|
||||
echo "/** \page" `basename $i .txt` > $INPUTFILE
|
||||
cat $i >>$INPUTFILE
|
||||
echo "*/" >>$INPUTFILE
|
||||
done
|
||||
|
||||
# Make some extra stuff to pass to doxygen
|
||||
# Input is kept as . because we cd to the input directory beforehand
|
||||
# This prevents doxygen from generating "documentation" for intermediate directories
|
||||
DOXYPARAMS=$(cat <<EOF
|
||||
PROJECT_NUMBER=2.0.0
|
||||
INPUT=.
|
||||
OUTPUT_DIRECTORY=$OUTPUTDIR
|
||||
QUIET=YES
|
||||
EOF
|
||||
);
|
||||
|
||||
# echo "$DOXYPARAMS"
|
||||
|
||||
# Clear out the output directory first
|
||||
find "${OUTPUTDIR}" -name "*.1" -delete
|
||||
|
||||
# Run doxygen
|
||||
cd "$TMPLOC"
|
||||
(cat "${DOXYFILE}" ; echo "$DOXYPARAMS";) | "$DOXYGENPATH" -
|
||||
|
||||
# Remember errors
|
||||
RESULT=$?
|
||||
|
||||
cd "${OUTPUTDIR}/man/man1/"
|
||||
if test "$RESULT" = 0 ; then
|
||||
|
||||
# Postprocess the files
|
||||
for i in "$INPUTDIR"/*.txt; do
|
||||
# It would be nice to use -i here for edit in place, but that is not portable
|
||||
CMD_NAME=`basename "$i" .txt`;
|
||||
sed -e "s/\(.\)\\.SH/\1/" -e "s/$CMD_NAME *\\\\- *\"\(.*\)\"/\1/" "${CMD_NAME}.1" > "${CMD_NAME}.1.tmp"
|
||||
mv "${CMD_NAME}.1.tmp" "${CMD_NAME}.1"
|
||||
done
|
||||
|
||||
# Erase condemned pages
|
||||
rm -f $CONDEMNED_PAGES
|
||||
|
||||
fi
|
||||
|
||||
# Destroy TMPLOC
|
||||
echo "Cleaning up '$TMPLOC'"
|
||||
rm -Rf "$TMPLOC"
|
||||
|
||||
if test "$RESULT" = 0; then
|
||||
# Tell the user what we did
|
||||
echo "Output man pages into '${OUTPUTDIR}'"
|
||||
else
|
||||
echo "Doxygen failed. See the output log for details."
|
||||
fi
|
||||
exit $RESULT
|
||||
@@ -1,62 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
{
|
||||
set -ex
|
||||
|
||||
lint=true
|
||||
if [ "$FISH_CHECK_LINT" = false ]; then
|
||||
lint=false
|
||||
fi
|
||||
|
||||
cargo_args=$FISH_CHECK_CARGO_ARGS
|
||||
target_triple=$FISH_CHECK_TARGET_TRIPLE
|
||||
if [ -n "$target_triple" ]; then
|
||||
cargo_args="$cargo_args --target=$FISH_CHECK_TARGET_TRIPLE"
|
||||
fi
|
||||
|
||||
cargo() {
|
||||
subcmd=$1
|
||||
shift
|
||||
# shellcheck disable=2086
|
||||
command cargo "$subcmd" $cargo_args "$@"
|
||||
}
|
||||
|
||||
cleanup () {
|
||||
if [ -n "$template_file" ] && [ -e "$template_file" ]; then
|
||||
rm "$template_file"
|
||||
fi
|
||||
}
|
||||
|
||||
trap cleanup EXIT INT TERM HUP
|
||||
|
||||
if $lint; then
|
||||
export RUSTFLAGS="--deny=warnings ${RUSTFLAGS}"
|
||||
export RUSTDOCFLAGS="--deny=warnings ${RUSTDOCFLAGS}"
|
||||
fi
|
||||
|
||||
workspace_root="$(dirname "$0")/.."
|
||||
target_dir=${CARGO_TARGET_DIR:-$workspace_root/target}
|
||||
if [ -n "$target_triple" ]; then
|
||||
target_dir="$target_dir/$target_triple"
|
||||
fi
|
||||
# The directory containing the binaries produced by cargo/rustc.
|
||||
# Currently, all builds are debug builds.
|
||||
build_dir="$target_dir/debug"
|
||||
|
||||
template_file=$(mktemp)
|
||||
FISH_GETTEXT_EXTRACTION_FILE=$template_file cargo build --workspace --all-targets --features=gettext-extract
|
||||
if $lint; then
|
||||
PATH="$build_dir:$PATH" "$workspace_root/build_tools/style.fish" --all --check
|
||||
for features in "" --no-default-features; do
|
||||
cargo clippy --workspace --all-targets $features
|
||||
done
|
||||
fi
|
||||
cargo test --no-default-features --workspace --all-targets
|
||||
cargo test --doc --workspace
|
||||
if $lint; then
|
||||
cargo doc --workspace
|
||||
fi
|
||||
FISH_GETTEXT_EXTRACTION_FILE=$template_file "$workspace_root/tests/test_driver.py" "$build_dir"
|
||||
|
||||
exit
|
||||
}
|
||||
3
build_tools/description-pak
Normal file
3
build_tools/description-pak
Normal file
@@ -0,0 +1,3 @@
|
||||
This is the_ridiculous'fish s delightful fork of, fish friendly interactive shell. For more information, visit http://ridiculousfish.com/shell/ .
|
||||
|
||||
This installer will install fish, but will not modify your /etc/shells file or your default shell. I trust you know how to do that yourself if you care to!
|
||||
@@ -1,53 +0,0 @@
|
||||
#!/usr/bin/env fish
|
||||
#
|
||||
# Compares the output of two fish profile runs and emits the time difference between
|
||||
# the first and second set of results.
|
||||
#
|
||||
# Usage: ./diff_profiles.fish profile1.log profile2.log > profile_diff.log
|
||||
|
||||
if test (count $argv) -ne 2
|
||||
|
||||
echo "Incorrect number of arguments."
|
||||
echo "Usage: "(status filename)" profile1.log profile2.log"
|
||||
exit 1
|
||||
end
|
||||
|
||||
set -l profile1 (cat $argv[1])
|
||||
set -l profile2 (cat $argv[2])
|
||||
|
||||
set -l line_no 0
|
||||
while set -l next_line_no (math $line_no + 1) && set -q profile1[$next_line_no] && set -q profile2[$next_line_no]
|
||||
set line_no $next_line_no
|
||||
|
||||
set -l line1 $profile1[$line_no]
|
||||
set -l line2 $profile2[$line_no]
|
||||
|
||||
if not string match -qr '^\s*\d+\s+\d+' $line1
|
||||
echo $line1
|
||||
continue
|
||||
end
|
||||
|
||||
set -l results1 (string match -r '^\s*(\d+)\s+(\d+)\s+(.*)' $line1)
|
||||
set -l results2 (string match -r '^\s*(\d+)\s+(\d+)\s+(.*)' $line2)
|
||||
|
||||
# times from both files
|
||||
set -l time1 $results1[2..3]
|
||||
set -l time2 $results2[2..3]
|
||||
|
||||
# leftover from both files
|
||||
set -l remainder1 $results1[4]
|
||||
set -l remainder2 $results2[4]
|
||||
|
||||
if not string match -q -- $remainder1 $remainder2
|
||||
echo Mismatch on line $line_no:
|
||||
echo - $remainder1
|
||||
echo + $remainder2
|
||||
exit 1
|
||||
end
|
||||
|
||||
set -l diff
|
||||
set diff[1] (math $time1[1] - $time2[1])
|
||||
set diff[2] (math $time1[2] - $time2[2])
|
||||
|
||||
printf '%10d %10d %s\n' $diff[1] $diff[2] $remainder1
|
||||
end
|
||||
@@ -1,23 +0,0 @@
|
||||
#!/usr/bin/env fish
|
||||
# Build a list of all sections in the html sphinx docs, separately by page,
|
||||
# so it can be added to share/functions/help.fish
|
||||
# Use like
|
||||
# fish extract_help_sections.fish user_doc/html/{fish_for_bash_users.html,faq.html,interactive.html,language.html,tutorial.html}
|
||||
# TODO: Currently `help` uses variable names we can't generate, so it needs to be touched up manually.
|
||||
# Also this could easily be broken by changes in sphinx, ideally we'd have a way to let it print the section titles.
|
||||
#
|
||||
|
||||
for file in $argv
|
||||
set -l varname (string replace -r '.*/(.*).html' '$1' -- $file | string escape --style=var)pages
|
||||
# Technically we can use any id in the document as an anchor, but listing them all is probably too much.
|
||||
# Sphinx stores section titles (in a slug-ized form) in the id,
|
||||
# and stores explicit section links in a `span` tag like
|
||||
# `<span id="identifiers"></span>`
|
||||
# We extract both separately.
|
||||
set -l sections (string replace -rf '.*class="headerlink" href="#([^"]*)".*' '$1' <$file)
|
||||
# Sections titled "id5" and such are internal cruft and shouldn't be offered.
|
||||
set -a sections (string replace -rf '.*span id="([^"]*)".*' '$1' <$file | string match -rv 'id\d+')
|
||||
|
||||
set sections (printf '%s\n' $sections | sort -u)
|
||||
echo set -l $varname $sections
|
||||
end
|
||||
@@ -1,139 +0,0 @@
|
||||
#!/usr/bin/env fish
|
||||
#
|
||||
# Tool to generate gettext messages template file.
|
||||
# Writes to stdout.
|
||||
# Intended to be called from `update_translations.fish`.
|
||||
|
||||
argparse use-existing-template= -- $argv
|
||||
or exit $status
|
||||
|
||||
begin
|
||||
# Write header. This is required by msguniq.
|
||||
# Note that this results in the file being overwritten.
|
||||
# This is desired behavior, to get rid of the results of prior invocations
|
||||
# of this script.
|
||||
begin
|
||||
echo 'msgid ""'
|
||||
echo 'msgstr ""'
|
||||
echo '"Content-Type: text/plain; charset=UTF-8\n"'
|
||||
echo ""
|
||||
end
|
||||
|
||||
set -g workspace_root (path resolve (status dirname)/..)
|
||||
|
||||
set -l rust_extraction_file
|
||||
if set -l --query _flag_use_existing_template
|
||||
set rust_extraction_file $_flag_use_existing_template
|
||||
else
|
||||
set rust_extraction_file (mktemp)
|
||||
# We need to build to ensure that the proc macro for extracting strings runs.
|
||||
FISH_GETTEXT_EXTRACTION_FILE=$rust_extraction_file cargo check --features=gettext-extract
|
||||
or exit 1
|
||||
end
|
||||
|
||||
echo '# fish-section-tier1-from-rust'
|
||||
# Get rid of duplicates and sort.
|
||||
msguniq --no-wrap --strict --sort-output $rust_extraction_file
|
||||
or exit 1
|
||||
|
||||
if not set -l --query _flag_use_existing_template
|
||||
rm $rust_extraction_file
|
||||
end
|
||||
|
||||
function extract_fish_script_messages_impl
|
||||
set -l regex $argv[1]
|
||||
set -e argv[1]
|
||||
# Using xgettext causes more trouble than it helps.
|
||||
# This is due to handling of escaping in fish differing from formats xgettext understands
|
||||
# (e.g. POSIX shell strings).
|
||||
# We work around this issue by manually writing the file content.
|
||||
|
||||
# Steps:
|
||||
# 1. We extract strings to be translated from the relevant files and drop the rest. This step
|
||||
# depends on the regex matching the entire line, and the first capture group matching the
|
||||
# string.
|
||||
# 2. We unescape. This gets rid of some escaping necessary in fish strings.
|
||||
# 3. The resulting strings are sorted alphabetically. This step is optional. Not sorting would
|
||||
# result in strings from the same file appearing together. Removing duplicates is also
|
||||
# optional, since msguniq takes care of that later on as well.
|
||||
# 4. Single backslashes are replaced by double backslashes. This results in the backslashes
|
||||
# being interpreted as literal backslashes by gettext tooling.
|
||||
# 5. Double quotes are escaped, such that they are not interpreted as the start or end of
|
||||
# a msgid.
|
||||
# 6. We transform the string into the format expected in a PO file.
|
||||
cat $argv |
|
||||
string replace --filter --regex $regex '$1' |
|
||||
string unescape |
|
||||
sort -u |
|
||||
sed -E -e 's_\\\\_\\\\\\\\_g' -e 's_"_\\\\"_g' -e 's_^(.*)$_msgid "\1"\nmsgstr ""\n_'
|
||||
end
|
||||
|
||||
function extract_fish_script_messages
|
||||
set -l tier $argv[1]
|
||||
set -e argv[1]
|
||||
if not set -q argv[1]
|
||||
return
|
||||
end
|
||||
# This regex handles explicit requests to translate a message. These are more important to translate
|
||||
# than messages which should be implicitly translated.
|
||||
set -l explicit_regex '.*\( *_ (([\'"]).+?(?<!\\\\)\\2) *\).*'
|
||||
echo "# fish-section-$tier-from-script-explicitly-added"
|
||||
extract_fish_script_messages_impl $explicit_regex $argv
|
||||
|
||||
# This regex handles descriptions for `complete` and `function` statements. These messages are not
|
||||
# particularly important to translate. Hence the "implicit" label.
|
||||
set -l implicit_regex '^(?:\s|and |or )*(?:complete|function).*? (?:-d|--description) (([\'"]).+?(?<!\\\\)\\2).*'
|
||||
echo "# fish-section-$tier-from-script-implicitly-added"
|
||||
extract_fish_script_messages_impl $implicit_regex $argv
|
||||
end
|
||||
|
||||
set -g share_dir $workspace_root/share
|
||||
|
||||
set -l tier1 $share_dir/config.fish
|
||||
set -l tier2
|
||||
set -l tier3
|
||||
|
||||
for file in $share_dir/completions/*.fish $share_dir/functions/*.fish
|
||||
# set -l tier (string match -r '^# localization: .*' <$file)
|
||||
set -l tier (string replace -rf -m1 \
|
||||
'^# localization: (.*)$' '$1' <$file)
|
||||
if set -q tier[1]
|
||||
switch "$tier"
|
||||
case tier1 tier2 tier3
|
||||
set -a $tier $file
|
||||
case 'skip*'
|
||||
case '*'
|
||||
echo >&2 "$file:1 unexpected localization tier: $tier"
|
||||
exit 1
|
||||
end
|
||||
continue
|
||||
end
|
||||
set -l dirname (path basename (path dirname $file))
|
||||
set -l command_name (path basename --no-extension $file)
|
||||
if test $dirname = functions &&
|
||||
string match -q -- 'fish_*' $command_name
|
||||
set -a tier1 $file
|
||||
continue
|
||||
end
|
||||
if test $dirname != completions
|
||||
echo >&2 "$file:1 missing localization tier for function file"
|
||||
exit 1
|
||||
end
|
||||
if test -e $workspace_root/doc_src/cmds/$command_name.rst
|
||||
set -a tier1 $file
|
||||
else
|
||||
set -a tier3 $file
|
||||
end
|
||||
end
|
||||
|
||||
extract_fish_script_messages tier1 $tier1
|
||||
extract_fish_script_messages tier2 $tier2
|
||||
extract_fish_script_messages tier3 $tier3
|
||||
end |
|
||||
# At this point, all extracted strings have been written to stdout,
|
||||
# starting with the ones taken from the Rust sources,
|
||||
# followed by strings explicitly marked for translation in fish scripts,
|
||||
# and finally the strings from fish scripts which get translated implicitly.
|
||||
# Because we do not eliminate duplicates across these categories,
|
||||
# we do it here, since other gettext tools expect no duplicates.
|
||||
msguniq --no-wrap
|
||||
@@ -1,70 +0,0 @@
|
||||
#!/bin/sh
|
||||
# Originally from the git sources (GIT-VERSION-GEN)
|
||||
# Presumably (C) Junio C Hamano <junkio@cox.net>
|
||||
# Reused under GPL v2.0
|
||||
# Modified for fish by David Adam <zanchey@ucc.gu.uwa.edu.au>
|
||||
|
||||
set -e
|
||||
|
||||
# Find the fish directory as two levels up from script directory.
|
||||
FISH_BASE_DIR="$( cd "$( dirname "$( dirname "$0" )" )" && pwd )"
|
||||
DEF_VER=unknown
|
||||
git_permission_failed=0
|
||||
|
||||
# First see if there is a version file (included in release tarballs),
|
||||
# then try git-describe, then default.
|
||||
if test -f version
|
||||
then
|
||||
VN=$(cat version) || VN="$DEF_VER"
|
||||
else
|
||||
if VN=$(git -C "$FISH_BASE_DIR" describe --always --dirty 2>/dev/null); then
|
||||
:
|
||||
else
|
||||
if test $? = 128; then
|
||||
# Current git versions return status 128
|
||||
# when run in a repo owned by another user.
|
||||
# Even for describe and everything.
|
||||
# This occurs for `sudo make install`.
|
||||
git_permission_failed=1
|
||||
fi
|
||||
VN="$DEF_VER"
|
||||
fi
|
||||
fi
|
||||
|
||||
# If the first param is --stdout, then output to stdout and exit.
|
||||
if test "$1" = '--stdout'
|
||||
then
|
||||
echo $VN
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Set the output directory as either the first param or cwd.
|
||||
test -n "$1" && OUTPUT_DIR=$1/ || OUTPUT_DIR=
|
||||
FBVF="${OUTPUT_DIR}FISH-BUILD-VERSION-FILE"
|
||||
|
||||
if test "$VN" = unknown && test -r "$FBVF" && test "$git_permission_failed" = 1
|
||||
then
|
||||
# HACK: Git failed, so we keep the current version file.
|
||||
# This helps in case you built fish as a normal user
|
||||
# and then try to `sudo make install` it.
|
||||
date +%s > "${OUTPUT_DIR}"fish-build-version-witness.txt
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if test -r "$FBVF"
|
||||
then
|
||||
VC=$(cat "$FBVF")
|
||||
else
|
||||
VC="unset"
|
||||
fi
|
||||
|
||||
# Maybe output the FBVF
|
||||
# It looks like "2.7.1-621-ga2f065e6"
|
||||
test "$VN" = "$VC" || {
|
||||
echo >&2 "$VN"
|
||||
echo "$VN" >"$FBVF"
|
||||
}
|
||||
|
||||
# Output the fish-build-version-witness.txt
|
||||
# See https://cmake.org/cmake/help/v3.4/policy/CMP0058.html
|
||||
date +%s > "${OUTPUT_DIR}"fish-build-version-witness.txt
|
||||
@@ -1,392 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>platform-application</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.no-container</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.container-manager</key>
|
||||
<true/>
|
||||
<key>com.apple.private.skip-library-validation</key>
|
||||
<true/>
|
||||
<key>com.apple.private.MobileContainerManager.allowed</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.adprivacyd</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.amfid</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.AppBundles</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.AppDataContainers</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.automation-mode</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.Biome</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.Calendar</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.CallHistory</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.CarrierBundles</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.chronod</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.CloudDocsDB</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.CloudKit</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.containers</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.CoreFollowUp</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.CoreKnowledge</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.Cryptex</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.demo_backup</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.DocumentRevisions</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.DumpPanic</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.ExposureNotification</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.FaceTime</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.familycircled</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.FindMy</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.fpsd</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.Health</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.HomeAI</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.HomeKit</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.iCloudDrive</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.idcredd</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.IdentityServices</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.kbd</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.Keychains</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.Lockdown</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.Mail</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.Messages</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.MessagesMetaData</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.MobileContainerManager</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.MobileDocuments</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.MobileIdentityService</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.mobilesync</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.multimodalsearchd</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.NanoTimeKit.FaceSupport</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.News</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.Notes</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.Photos</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.PhotosLibraries</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.pipelined</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.preferences</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.PrivacyAccounting</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.Safari</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.SearchParty</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.SecureElementService</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.SensorKit</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.SFAnalytics</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.SiriInference</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.SiriReferenceResolution</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.SiriVocabulary</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.SoC</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.SpeechPersonalizedLM</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.Spotlight</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.StatusKit</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.Stocks</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.Suggestions</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.SymptomFramework</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.sysdagnose.ScreenshotServicesService</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.TCC</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.TimeMachine</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.triald</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.trustd</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.trustd-private</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.universalaccess</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.Voicemail</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.Wireless</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.disk-device-access</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.ane_model_cache</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.apfs_boot_mount</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.clientScripter</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.com.apple.mediaanalysisd</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.com.apple.MobileAsset.CarPlayAppBlacklist</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.com.apple.MobileAsset.DeviceCheck</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.com.apple.MobileAsset.DictionaryServices.dictionary2</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.com.apple.MobileAsset.DuetExpertCenterAsset</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.com.apple.MobileAsset.EmbeddedNL</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.com.apple.MobileAsset.Font5</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.com.apple.MobileAsset.Font6</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.com.apple.MobileAsset.HealthKt.FeatureAvailability</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.com.apple.MobileAsset.HomeKit</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.com.apple.MobileAsset.MacinTalkVoiceAssets</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.com.apple.MobileAsset.MailDynamicData</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.com.apple.MobileAsset.MXLongFormVideoApps</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.com.apple.MobileAsset.network.networknomicon</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.com.apple.MobileAsset.PKITrustSupplementals</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.com.apple.MobileAsset.SharingDeviceAssets</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.com.apple.MobileAsset.SiriShortcutsMobileAsset</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.com.apple.MobileAsset.TimeZoneUpdate</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.com.apple.MobileAsset.VoiceServices.CombinedVocalizerVoices</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.com.apple.MobileAsset.VoiceServices.CustomVoice</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.com.apple.MobileAsset.VoiceServices.GryphonVoice</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.com.apple.MobileAsset.VoiceServicesVocalizerVoice</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.com.apple.MobileAsset.VoiceServices.VoiceResources</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.com.apple.MobileAsset.VoiceTriggerAssets</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.CoreAnalytics</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.coreduet_knowledge_store</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.coreidvd</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.coreknowledge</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.CoreRoutine</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.CoreSpeech</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.dmd</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.dprivacyd_storage</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.ExtensibleSSO</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.facekit</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.fpsd</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.MobileStorageMounter</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.MusicApp</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.nsurlsessiond</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.pearl-field-diagnostics</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.proactivepredictions</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.QLThumbnailCache</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.remotemanagementd</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.RoleAccountStaging</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.sensorkit</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.shortcuts</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.siriremembers</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.timezone</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.triald</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.voiceshortcuts</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage-exempt.heritable</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.AppleMediaServices</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.ContactlessReader</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.CoreRoutine</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.DiagnosticReports</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.DiagnosticReports.read-write</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.DoNotDisturb</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.Home</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.IntelligencePlatform</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.Location</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.ManagedConfiguration</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.MapsSync</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.MobileBackup</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.MobileStorageMounter</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.PassKit</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.SiriFeatureStore</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.SiriSELF</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.SoundProfileAsset</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.TextUnderstanding</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.Weather</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.appleaccountd</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.ciconia</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.clipserviced</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.coreduet_knowledge_store</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.driverkitd</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.geoanalyticsd</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.geod</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.launchd</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.sessionkitd</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.sysdiagnose.ScreenshotServicesService</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.sysdiagnose.sysdiagnose</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.storage.tmp</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.critical</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.datavault.metadata</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.install</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.install.heritable</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.restricted-block-devices</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.MobileAssetDownload</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.amsengagementd</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.com.apple.MobileAsset.HealthKit.FeatureAvailability</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.com.apple.MobileAsset.Trial.Siri.SiriDialogAssets</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.com.apple.MobileAsset.Trial.Siri.SiriExperienceCam</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.com.apple.MobileAsset.Trial.Siri.SiriFindMyConfigurationFiles</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.com.apple.MobileAsset.Trial.Siri.SiriInferredHelpfulness</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.com.apple.MobileAsset.Trial.Siri.SiriTextToSpeech</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.com.apple.MobileAsset.Trial.Siri.SiriUnderstandingAsrAssistant</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.com.apple.MobileAsset.Trial.Siri.SiriUnderstandingAsrHammer</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.com.apple.MobileAsset.Trial.Siri.SiriUnderstandingAsrUaap</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.com.apple.MobileAsset.Trial.Siri.SiriUnderstandingAttentionAssets</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.com.apple.MobileAsset.Trial.Siri.SiriUnderstandingMorphun</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.com.apple.MobileAsset.Trial.Siri.SiriUnderstandingNL</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.com.apple.MobileAsset.Trial.Siri.SiriUnderstandingNLOverrides</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.coreparsec_feedbacks</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.coreparsec_uploadables</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.early_boot_mount</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.storage.screentime</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.volume.ISCRecovery</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.volume.Preboot</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.volume.Recovery</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.volume.Update</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.volume.VM</key>
|
||||
<true/>
|
||||
<key>com.apple.rootless.volume.iSCPreboot</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,36 +0,0 @@
|
||||
#! /usr/bin/env fish
|
||||
|
||||
set -l TAG $argv[1]
|
||||
|
||||
if test -z "$TAG"
|
||||
echo "Tag name required."
|
||||
exit 1
|
||||
end
|
||||
|
||||
if not contains -- $TAG (git tag)
|
||||
echo "$TAG is not a valid tag name."
|
||||
exit 1
|
||||
end
|
||||
|
||||
set -l committers_to_tag (mktemp)
|
||||
or exit 1
|
||||
set -l committers_from_tag (mktemp)
|
||||
or exit 1
|
||||
|
||||
# You might think it would be better to case-insensitively sort/compare the names
|
||||
# to produce a more natural-looking list.
|
||||
# Unicode collation tables mean that this is fraught with danger; for example, the
|
||||
# "“" character will not case-fold in UTF-8 locales. sort suggests using the C locale!
|
||||
|
||||
git log "$TAG" --format="%aN" --reverse | sort -u >$committers_to_tag
|
||||
git log "$TAG".. --format="%aN" --reverse | sort -u >$committers_from_tag
|
||||
|
||||
echo New committers:
|
||||
echo (comm -13 $committers_to_tag $committers_from_tag)','
|
||||
|
||||
echo
|
||||
|
||||
echo Returning committers:
|
||||
echo (comm -12 $committers_to_tag $committers_from_tag)','
|
||||
|
||||
rm $committers_to_tag $committers_from_tag
|
||||
@@ -1,3 +0,0 @@
|
||||
# LSAN can detect leaks tracing back to __asan::AsanThread::ThreadStart (probably caused by our
|
||||
# threads not exiting before their TLS dtors are called). Just ignore it.
|
||||
leak:AsanThread
|
||||
@@ -1,43 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Helper to notarize an .app.zip or .pkg file.
|
||||
|
||||
set -e
|
||||
|
||||
die() { echo "$*" 1>&2 ; exit 1; }
|
||||
|
||||
|
||||
test "$#" -ge 1 || die "No paths specified."
|
||||
|
||||
for INPUT in "$@"; do
|
||||
echo "Processing $INPUT"
|
||||
test -f "$INPUT" || die "Not a file: $INPUT"
|
||||
ext="${INPUT##*.}"
|
||||
{ test "$ext" = "zip" || test "$ext" = "pkg"; } || die "Unrecognized extension: $ext"
|
||||
|
||||
xcrun notarytool submit "$INPUT" --keychain-profile AC_PASSWORD --wait
|
||||
|
||||
if test "$ext" = "zip"; then
|
||||
TMPDIR=$(mktemp -d)
|
||||
echo "Extracting to $TMPDIR"
|
||||
unzip -q "$INPUT" -d "$TMPDIR"
|
||||
STAPLE_TARGET=$(echo "$TMPDIR"/*)
|
||||
else
|
||||
STAPLE_TARGET="$INPUT"
|
||||
fi
|
||||
echo "Stapling $STAPLE_TARGET"
|
||||
xcrun stapler staple "$STAPLE_TARGET"
|
||||
|
||||
if test "$ext" = "zip"; then
|
||||
# Zip it back up.
|
||||
INPUT_FULL=$(realpath "$INPUT")
|
||||
rm -f "$INPUT"
|
||||
cd "$(dirname "$STAPLE_TARGET")"
|
||||
zip -r -q "$INPUT_FULL" "$(basename "$STAPLE_TARGET")"
|
||||
fi
|
||||
echo "Processed $INPUT"
|
||||
|
||||
if test "$ext" = "zip"; then
|
||||
spctl -a -v "$STAPLE_TARGET"
|
||||
fi
|
||||
done
|
||||
8
build_tools/make_csv_completions.fish
Executable file
8
build_tools/make_csv_completions.fish
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env fish
|
||||
#
|
||||
# This file produces command specific completions for csv. Meant to be executed
|
||||
# from the root directory (so the completions get put in the right place).
|
||||
|
||||
. build_tools/make_vcs_completions_generic.fish
|
||||
|
||||
write_completions csv >share/completions/csv.fish
|
||||
12
build_tools/make_darcs_completions.fish
Executable file
12
build_tools/make_darcs_completions.fish
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/usr/bin/env fish
|
||||
#
|
||||
# This file produces command specific completions for darcs. Meant to be
|
||||
# executed from the root directory (so the completions get put in the right
|
||||
# place).
|
||||
|
||||
. build_tools/make_vcs_completions_generic.fish
|
||||
|
||||
set darcs_comp 'complete -c darcs -n "not __fish_use_subcommand" -a "(test -f _darcs/prefs/repos; and cat _darcs/prefs/repos)" --description "Darcs repo"'
|
||||
set darcs_comp $darcs_comp 'complete -c darcs -a "test predist boringfile binariesfile" -n "contains setpref (commandline -poc)" --description "Set the specified option" -x'
|
||||
|
||||
write_completions darcs $darcs_comp >share/completions/darcs.fish
|
||||
18
build_tools/make_deb.sh
Executable file
18
build_tools/make_deb.sh
Executable file
@@ -0,0 +1,18 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Terminate on error
|
||||
set -e
|
||||
|
||||
sudo rm -Rf /tmp/fishfish
|
||||
mkdir /tmp/fishfish
|
||||
git archive --format=tar fish_fish | tar -x -C /tmp/fishfish
|
||||
mkdir /tmp/fishfish/doc-pak
|
||||
cp README INSTALL CHANGELOG release_notes.html /tmp/fishfish/doc-pak/
|
||||
cp build_tools/description-pak /tmp/fishfish/
|
||||
cd /tmp/fishfish
|
||||
autoconf
|
||||
./configure
|
||||
make -j 3
|
||||
sudo checkinstall --default --pakdir ~/fish_built/ --pkgversion 0.9 make install
|
||||
mv ~/fish_built/fishfish_0.9-1_i386.deb ~/fish_built/fishfish_0.9_i386.deb
|
||||
|
||||
8
build_tools/make_hg_completions.fish
Executable file
8
build_tools/make_hg_completions.fish
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env fish
|
||||
#
|
||||
# This file produces command specific completions for hg. Meant to be executed
|
||||
# from the root directory (so the completions get put in the right place).
|
||||
|
||||
. build_tools/make_vcs_completions_generic.fish
|
||||
|
||||
write_completions hg >share/completions/hg.fish
|
||||
@@ -1,177 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Script to produce an OS X installer .pkg and .app(.zip)
|
||||
|
||||
usage() {
|
||||
echo "Build macOS packages, optionally signing and notarizing them."
|
||||
echo "Usage: $0 options"
|
||||
echo "Options:"
|
||||
echo " -s Enables code signing"
|
||||
echo " -f <APP_KEY.p12> Path to .p12 file for application signing"
|
||||
echo " -i <INSTALLER_KEY.p12> Path to .p12 file for installer signing"
|
||||
echo " -p <PASSWORD> Password for the .p12 files (necessary to access the certificates)"
|
||||
echo " -e <entitlements file> (Optional) Path to an entitlements XML file"
|
||||
echo " -n Enables notarization. This will fail if code signing is not also enabled."
|
||||
echo " -j <API_KEY.JSON> Path to JSON file generated with \`rcodesign encode-app-store-connect-api-key\` (required for notarization)"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
set -x
|
||||
set -e
|
||||
|
||||
SIGN=
|
||||
NOTARIZE=
|
||||
|
||||
ARM64_DEPLOY_TARGET='MACOSX_DEPLOYMENT_TARGET=11.0'
|
||||
X86_64_DEPLOY_TARGET='MACOSX_DEPLOYMENT_TARGET=10.9'
|
||||
|
||||
# As of this writing, the most recent Rust release supports macOS back to 10.12.
|
||||
# The first supported version of macOS on arm64 is 10.15, so any Rust is fine for arm64.
|
||||
# We wish to support back to 10.9 on x86-64; the last version of Rust to support that is
|
||||
# version 1.73.0.
|
||||
RUST_VERSION_X86_64=1.70.0
|
||||
|
||||
while getopts "sf:i:p:e:nj:" opt; do
|
||||
case $opt in
|
||||
s) SIGN=1;;
|
||||
f) P12_APP_FILE=$(realpath "$OPTARG");;
|
||||
i) P12_INSTALL_FILE=$(realpath "$OPTARG");;
|
||||
p) P12_PASSWORD="$OPTARG";;
|
||||
e) ENTITLEMENTS_FILE=$(realpath "$OPTARG");;
|
||||
n) NOTARIZE=1;;
|
||||
j) API_KEY_FILE=$(realpath "$OPTARG");;
|
||||
\?) usage;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ -n "$SIGN" ] && { [ -z "$P12_APP_FILE" ] || [ -z "$P12_INSTALL_FILE" ] || [ -z "$P12_PASSWORD" ]; }; then
|
||||
usage
|
||||
fi
|
||||
|
||||
if [ -n "$NOTARIZE" ] && [ -z "$API_KEY_FILE" ]; then
|
||||
usage
|
||||
fi
|
||||
|
||||
VERSION=$(build_tools/git_version_gen.sh --stdout 2>/dev/null)
|
||||
|
||||
echo "Version is $VERSION"
|
||||
|
||||
PKGDIR=$(mktemp -d)
|
||||
echo "$PKGDIR"
|
||||
|
||||
SRC_DIR=$PWD
|
||||
OUTPUT_PATH=${FISH_ARTEFACT_PATH:-~/fish_built}
|
||||
|
||||
mkdir -p "$PKGDIR/build_x86_64" "$PKGDIR/build_arm64" "$PKGDIR/root" "$PKGDIR/intermediates" "$PKGDIR/dst"
|
||||
|
||||
# Build and install for arm64.
|
||||
# Pass FISH_USE_SYSTEM_PCRE2=OFF because a system PCRE2 on macOS will not be signed by fish,
|
||||
# and will probably not be built universal, so the package will fail to validate/run on other systems.
|
||||
# Note CMAKE_OSX_ARCHITECTURES is still relevant for the Mac app.
|
||||
{ cd "$PKGDIR/build_arm64" \
|
||||
&& cmake \
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DCMAKE_EXE_LINKER_FLAGS="-Wl,-ld_classic" \
|
||||
-DRust_CARGO_TARGET=aarch64-apple-darwin \
|
||||
-DCMAKE_OSX_ARCHITECTURES='arm64;x86_64' \
|
||||
-DFISH_USE_SYSTEM_PCRE2=OFF \
|
||||
"$SRC_DIR" \
|
||||
&& env $ARM64_DEPLOY_TARGET make VERBOSE=1 -j 12 \
|
||||
&& env DESTDIR="$PKGDIR/root/" $ARM64_DEPLOY_TARGET make install;
|
||||
}
|
||||
|
||||
# Build for x86-64 but do not install; instead we will make some fat binaries inside the root.
|
||||
# Set RUST_VERSION_X86_64 to the last version of Rust that supports macOS 10.9.
|
||||
{ cd "$PKGDIR/build_x86_64" \
|
||||
&& cmake \
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DCMAKE_EXE_LINKER_FLAGS="-Wl,-ld_classic" \
|
||||
-DRust_TOOLCHAIN="$RUST_VERSION_X86_64" \
|
||||
-DRust_CARGO_TARGET=x86_64-apple-darwin \
|
||||
-DRust_COMPILER="$(rustup +$RUST_VERSION_X86_64 which rustc)" \
|
||||
-DRust_CARGO="$(rustup +$RUST_VERSION_X86_64 which cargo)" \
|
||||
-DCMAKE_OSX_ARCHITECTURES='arm64;x86_64' \
|
||||
-DFISH_USE_SYSTEM_PCRE2=OFF "$SRC_DIR" \
|
||||
&& env $X86_64_DEPLOY_TARGET make VERBOSE=1 -j 12; }
|
||||
|
||||
# Fatten them up.
|
||||
for FILE in "$PKGDIR"/root/usr/local/bin/*; do
|
||||
X86_FILE="$PKGDIR/build_x86_64/$(basename "$FILE")"
|
||||
rcodesign macho-universal-create --output "$FILE" "$FILE" "$X86_FILE"
|
||||
chmod 755 "$FILE"
|
||||
done
|
||||
|
||||
if test -n "$SIGN"; then
|
||||
echo "Signing executables"
|
||||
ARGS=(
|
||||
--p12-file "$P12_APP_FILE"
|
||||
--p12-password "$P12_PASSWORD"
|
||||
--code-signature-flags runtime
|
||||
--for-notarization
|
||||
)
|
||||
if [ -n "$ENTITLEMENTS_FILE" ]; then
|
||||
ARGS+=(--entitlements-xml-file "$ENTITLEMENTS_FILE")
|
||||
fi
|
||||
for FILE in "$PKGDIR"/root/usr/local/bin/*; do
|
||||
(set +x; rcodesign sign "${ARGS[@]}" "$FILE")
|
||||
done
|
||||
fi
|
||||
|
||||
pkgbuild --scripts "$SRC_DIR/build_tools/osx_package_scripts" --root "$PKGDIR/root/" --identifier 'com.ridiculousfish.fish-shell-pkg' --version "$VERSION" "$PKGDIR/intermediates/fish.pkg"
|
||||
productbuild --package-path "$PKGDIR/intermediates" --distribution "$SRC_DIR/build_tools/osx_distribution.xml" --resources "$SRC_DIR/build_tools/osx_package_resources/" "$OUTPUT_PATH/fish-$VERSION.pkg"
|
||||
|
||||
if test -n "$SIGN"; then
|
||||
echo "Signing installer"
|
||||
ARGS=(
|
||||
--p12-file "$P12_INSTALL_FILE"
|
||||
--p12-password "$P12_PASSWORD"
|
||||
--code-signature-flags runtime
|
||||
--for-notarization
|
||||
)
|
||||
(set +x; rcodesign sign "${ARGS[@]}" "$OUTPUT_PATH/fish-$VERSION.pkg")
|
||||
fi
|
||||
|
||||
# Make the app
|
||||
(cd "$PKGDIR/build_arm64" && env $ARM64_DEPLOY_TARGET make -j 12 fish_macapp)
|
||||
(cd "$PKGDIR/build_x86_64" && env $X86_64_DEPLOY_TARGET make -j 12 fish_macapp)
|
||||
|
||||
# Make the app's /usr/local/bin binaries universal. Note fish.app/Contents/MacOS/fish already is, courtesy of CMake.
|
||||
cd "$PKGDIR/build_arm64"
|
||||
for FILE in fish.app/Contents/Resources/base/usr/local/bin/*; do
|
||||
X86_FILE="$PKGDIR/build_x86_64/fish.app/Contents/Resources/base/usr/local/bin/$(basename "$FILE")"
|
||||
rcodesign macho-universal-create --output "$FILE" "$FILE" "$X86_FILE"
|
||||
|
||||
# macho-universal-create screws up the permissions.
|
||||
chmod 755 "$FILE"
|
||||
done
|
||||
|
||||
if test -n "$SIGN"; then
|
||||
echo "Signing app"
|
||||
ARGS=(
|
||||
--p12-file "$P12_APP_FILE"
|
||||
--p12-password "$P12_PASSWORD"
|
||||
--code-signature-flags runtime
|
||||
--for-notarization
|
||||
)
|
||||
if [ -n "$ENTITLEMENTS_FILE" ]; then
|
||||
ARGS+=(--entitlements-xml-file "$ENTITLEMENTS_FILE")
|
||||
fi
|
||||
(set +x; rcodesign sign "${ARGS[@]}" "fish.app")
|
||||
|
||||
fi
|
||||
|
||||
cp -R "fish.app" "$OUTPUT_PATH/fish-$VERSION.app"
|
||||
cd "$OUTPUT_PATH"
|
||||
|
||||
# Maybe notarize.
|
||||
if test -n "$NOTARIZE"; then
|
||||
echo "Notarizing"
|
||||
rcodesign notarize --staple --wait --max-wait-seconds 1800 --api-key-file "$API_KEY_FILE" "$OUTPUT_PATH/fish-$VERSION.pkg"
|
||||
rcodesign notarize --staple --wait --max-wait-seconds 1800 --api-key-file "$API_KEY_FILE" "$OUTPUT_PATH/fish-$VERSION.app"
|
||||
fi
|
||||
|
||||
# Zip it up.
|
||||
zip -r "fish-$VERSION.app.zip" "fish-$VERSION.app" && rm -Rf "fish-$VERSION.app"
|
||||
|
||||
rm -rf "$PKGDIR"
|
||||
@@ -1 +0,0 @@
|
||||
make_macos_pkg.sh
|
||||
23
build_tools/make_pkg.sh
Executable file
23
build_tools/make_pkg.sh
Executable file
@@ -0,0 +1,23 @@
|
||||
#!/bin/sh
|
||||
|
||||
VERSION=`sed -E -n 's/^.*PACKAGE_VERSION "([0-9.]+)"/\1/p' osx/config.h`
|
||||
if test -z "$VERSION" ; then
|
||||
echo "Could not get version from osx/config.h"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Version is $VERSION"
|
||||
|
||||
set -x
|
||||
|
||||
make distclean
|
||||
rm -Rf /tmp/fish_pkg
|
||||
|
||||
#Exit on error
|
||||
set -e
|
||||
|
||||
mkdir -p /tmp/fish_pkg/root /tmp/fish_pkg/intermediates /tmp/fish_pkg/dst
|
||||
xcodebuild install -scheme install_tree -configuration Release DSTROOT=/tmp/fish_pkg/root/
|
||||
pkgbuild --scripts build_tools/osx_package_scripts --root /tmp/fish_pkg/root/ --identifier 'com.ridiculousfish.fish-shell-pkg' --version "$VERSION" /tmp/fish_pkg/intermediates/fish.pkg
|
||||
|
||||
productbuild --package-path /tmp/fish_pkg/intermediates --distribution build_tools/osx_distribution.xml --resources build_tools/osx_package_resources/ ~/fish_built/fish.pkg
|
||||
8
build_tools/make_svn_completions.fish
Executable file
8
build_tools/make_svn_completions.fish
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env fish
|
||||
#
|
||||
# This file produces command specific completions for svn. Meant to be executed
|
||||
# from the root directory (so the completions get put in the right place).
|
||||
|
||||
. build_tools/make_vcs_completions_generic.fish
|
||||
|
||||
write_completions svn >share/completions/svn.fish
|
||||
@@ -2,78 +2,45 @@
|
||||
|
||||
# Script to generate a tarball
|
||||
# We use git to output a tree. But we also want to build the user documentation
|
||||
# and put that in the tarball, so that nobody needs to have sphinx installed
|
||||
# and put that in the tarball, so that nobody needs to have doxygen installed
|
||||
# to build it.
|
||||
# Outputs to $FISH_ARTEFACT_PATH or ~/fish_built by default
|
||||
|
||||
# Exit on error
|
||||
set -e
|
||||
|
||||
# We will generate a tarball with a prefix "fish-VERSION"
|
||||
# We wil generate a tarball with a prefix "fish"
|
||||
# git can do that automatically for us via git-archive
|
||||
# but to get the documentation in, we need to make a symlink called "fish-VERSION"
|
||||
# but to get the documentation in, we need to make a symlink called "fish"
|
||||
# and tar from that, so that the documentation gets the right prefix
|
||||
|
||||
# Use Ninja if available, as it automatically parallelises
|
||||
BUILD_TOOL="make"
|
||||
BUILD_GENERATOR="Unix Makefiles"
|
||||
if command -v ninja >/dev/null; then
|
||||
BUILD_TOOL="ninja"
|
||||
BUILD_GENERATOR="Ninja"
|
||||
fi
|
||||
|
||||
# We need GNU tar as that supports the --mtime and --transform options
|
||||
TAR=notfound
|
||||
for try in tar gtar gnutar; do
|
||||
if $try -Pcf /dev/null --mtime now /dev/null >/dev/null 2>&1; then
|
||||
TAR=$try
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$TAR" = "notfound" ]; then
|
||||
echo 'No suitable tar (supporting --mtime) found as tar/gtar/gnutar in PATH'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Get the current directory, which we'll use for symlinks
|
||||
wd="$PWD"
|
||||
|
||||
# Get the version
|
||||
VERSION=$(build_tools/git_version_gen.sh --stdout 2>/dev/null)
|
||||
|
||||
# The name of the prefix, which is the directory that you get when you untar
|
||||
prefix="fish-$VERSION"
|
||||
prefix="fish"
|
||||
|
||||
# The path where we will output the tar file
|
||||
# Defaults to ~/fish_built
|
||||
path=${FISH_ARTEFACT_PATH:-~/fish_built}/$prefix.tar
|
||||
path=~/fish_built/fish-2.0.tar
|
||||
|
||||
# Clean up stuff we've written before
|
||||
rm -f "$path" "$path".xz
|
||||
rm -f "$path" "$path".gz
|
||||
|
||||
# git starts the archive
|
||||
git archive --format=tar --prefix="$prefix"/ HEAD > "$path"
|
||||
git archive --format=tar --prefix="$prefix"/ master > "$path"
|
||||
|
||||
# tarball out the documentation, generate a version file
|
||||
PREFIX_TMPDIR=$(mktemp -d)
|
||||
cd "$PREFIX_TMPDIR"
|
||||
echo "$VERSION" > version
|
||||
cmake -G "$BUILD_GENERATOR" -DCMAKE_BUILD_TYPE=Debug "$wd"
|
||||
$BUILD_TOOL doc
|
||||
# tarball out the documentation
|
||||
make user_doc
|
||||
make share/man
|
||||
cd /tmp
|
||||
rm -f "$prefix"
|
||||
ln -s "$wd" "$prefix"
|
||||
tar --append --file="$path" "$prefix"/user_doc/html
|
||||
tar --append --file="$path" "$prefix"/share/man
|
||||
rm -f "$prefix"
|
||||
|
||||
TAR_APPEND="$TAR --append --file=$path --mtime=now --owner=0 --group=0 \
|
||||
--mode=g+w,a+rX --transform s/^/$prefix\//"
|
||||
$TAR_APPEND --no-recursion user_doc
|
||||
$TAR_APPEND user_doc/html user_doc/man
|
||||
$TAR_APPEND version
|
||||
# gzip it
|
||||
gzip "$path"
|
||||
|
||||
cd -
|
||||
rm -r "$PREFIX_TMPDIR"
|
||||
|
||||
# xz it
|
||||
xz "$path"
|
||||
|
||||
# Output what we did, and the sha256 hash
|
||||
echo "Tarball written to $path".xz
|
||||
openssl dgst -sha256 "$path".xz
|
||||
# Output what we did, and the sha1 hash
|
||||
echo "Tarball written to $path".gz
|
||||
openssl sha1 "$path".gz
|
||||
|
||||
9
build_tools/make_vcs_completions.fish
Executable file
9
build_tools/make_vcs_completions.fish
Executable file
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env fish
|
||||
#
|
||||
# This file produces command specific completions for hg, darcs and a
|
||||
# few other vcs systems.
|
||||
|
||||
build_tools/make_darcs_completions.fish
|
||||
build_tools/make_hg_completions.fish
|
||||
build_tools/make_svn_completions.fish
|
||||
build_tools/make_csv_completions.fish
|
||||
229
build_tools/make_vcs_completions_generic.fish
Executable file
229
build_tools/make_vcs_completions_generic.fish
Executable file
@@ -0,0 +1,229 @@
|
||||
#!/usr/bin/env fish
|
||||
#
|
||||
# This file provides generic functions for generating specific completions for
|
||||
# hg, darcs and a few other vcs systems. It uses the fact that all these
|
||||
# systems have a somewhat uniform command line help mechanism.
|
||||
#
|
||||
|
||||
function cap
|
||||
set res (echo $argv |cut -c 1|tr a-z A-Z)(echo $argv |cut -c 2-)
|
||||
echo $res
|
||||
end
|
||||
|
||||
#
|
||||
# Escapes the single quote (') character and removes trailing whitespace from $argv
|
||||
#
|
||||
|
||||
function esc
|
||||
echo $argv | sed -e "s/\(['\\\]\)/\\\\\1/g" | sed -e 's/ *$//' | sed -e 's/ .*//'
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# This function formats a list of completion information into a set of fish completions
|
||||
#
|
||||
# The first argument is the condition string, which will be copied to
|
||||
# the resulting commandline verbatim
|
||||
#
|
||||
# Remaining arguments are tab separated lists of completion
|
||||
# information. Each list contains four elements, the short switch, the
|
||||
# long switch, the argument and the description.
|
||||
#
|
||||
|
||||
function complete_from_list
|
||||
|
||||
set condition $argv[1]
|
||||
set -e argv[1]
|
||||
|
||||
for j in $argv
|
||||
set exploded (echo $j|tr \t \n)
|
||||
set short $exploded[1]
|
||||
set long $exploded[2]
|
||||
set arg $exploded[3]
|
||||
set desc (cap (esc $exploded[4]))
|
||||
|
||||
set str
|
||||
|
||||
switch $short
|
||||
case '-?'
|
||||
set str $str -s (printf "%s\n" $short|cut -c 2)
|
||||
end
|
||||
|
||||
switch $long
|
||||
case '--?*'
|
||||
set str $str -l (printf "%s\n" $long|cut -c 3-)
|
||||
end
|
||||
|
||||
switch $arg
|
||||
case '=DIRECTORY' ' dir'
|
||||
set str $str -x -a "'(__fish_complete_directories (commandline -ct))'"
|
||||
|
||||
case '=COMMAND'
|
||||
set str $str -x -a "'(__fish_complete_command)'"
|
||||
|
||||
case '=USERNAME' ' <user>'
|
||||
set str $str -x -a "'(__fish_complete_users)'"
|
||||
|
||||
case '=FILENAME' '=FILE' ' <file>'
|
||||
set str $str -r
|
||||
|
||||
case ' arg'
|
||||
set str $str -x
|
||||
|
||||
case ' (*):'
|
||||
set str $str -x -a \'(echo $arg| sed -e "s/ (\(.*\)):/\1/" |tr '/' ' ')\'
|
||||
|
||||
case '?*'
|
||||
set str $str -x
|
||||
if not set -q unknown
|
||||
set -g unknown
|
||||
end
|
||||
if not contains $arg $unknown
|
||||
echo "Don't know how to handle arguments of type '$arg'" >&2
|
||||
set unknown $unknown $arg
|
||||
end
|
||||
end
|
||||
|
||||
switch $desc
|
||||
case '?*'
|
||||
set str $str --description \'$desc\'
|
||||
end
|
||||
|
||||
echo complete -c $cmd $condition $str
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
function write_completions
|
||||
|
||||
set -g cmd $argv[1]; or return 1
|
||||
|
||||
echo "Making completions for $cmd" >&2
|
||||
|
||||
|
||||
echo '
|
||||
#
|
||||
# Completions for the '$cmd' command
|
||||
# This file was autogenerated by the file make_vcs_completions.fish
|
||||
# which is shipped with the fish source code.
|
||||
#
|
||||
|
||||
#
|
||||
# Completions from commandline
|
||||
#
|
||||
'
|
||||
set -e argv[1]
|
||||
|
||||
while count $argv >/dev/null
|
||||
echo $argv[1]
|
||||
set -e argv[1]
|
||||
end
|
||||
|
||||
|
||||
eval "function cmd; $cmd \$argv; end"
|
||||
|
||||
set -l cmd_str
|
||||
|
||||
switch $cmd
|
||||
case svn
|
||||
|
||||
function list_subcommand
|
||||
set cmd1 '\([^ ]*\)'
|
||||
set cmd2 '\([^,)]*\)'
|
||||
set cmdn '\(, \([^,)]*\)\|\)'
|
||||
set svn_re '^ *'$cmd1'\( ('$cmd2$cmdn$cmdn')\|\).*$'
|
||||
cmd help|sed -ne 's/'$svn_re'/\1\n\3\n\5\n\7/p'| grep .
|
||||
end
|
||||
|
||||
function list_subcommand_help
|
||||
set short_exp '\(-.\|\)'
|
||||
set long_exp '\(--[^ =,]*\)'
|
||||
set arg_exp '\(\|[= ][^ ][^ ]*\)'
|
||||
set desc_exp '\([\t ]*:[\t ]*\|\)\([^ ].*[^.]\)'
|
||||
set re "^ *$short_exp *$long_exp$arg_exp *$desc_exp\(\|\\.\)\$"
|
||||
cmd help $argv | sed -n -e 's/'$re'/\1\t\2\t\3\t\5/p'
|
||||
end
|
||||
|
||||
for i in (list_subcommand)
|
||||
|
||||
set desc (cmd help $i|head -n 3| sed -e 's/usage:.*//'| tr \n \ | sed -e 's/[^:]*: *\(.*[^.]\)\(\|\\.\)$/\1/')
|
||||
set desc (esc $desc)
|
||||
set cmd_str $cmd_str "-a $i --description '$desc'"
|
||||
end
|
||||
|
||||
case cvs
|
||||
|
||||
function list_subcommand
|
||||
cmd --help-commands 2>| sed -n -e 's/^ *\([^ ][^ ]*\) .*$/\1/p'
|
||||
end
|
||||
|
||||
set short_exp '\(-.\)'
|
||||
set arg_exp '\(\| [^ \t][^ \t]*\)'
|
||||
set desc_exp '\([\t ]*:[\t ]*\|\)\([^ ].*\)'
|
||||
set -g re '^[ \t]*'$short_exp$arg_exp'[ \t]*'$desc_exp'$'
|
||||
|
||||
function list_subcommand_help
|
||||
#'s/^[ \t]*\(-.\)[ \t]\([^- \t][^ \t]*\)*[ \t]*\([^-].*\)$/\1\t\2\t\3/p'
|
||||
|
||||
cmd -H $argv 2>| sed -n -e 's/'$re'/\1\t\t\2\t\4/p'
|
||||
end
|
||||
|
||||
echo '
|
||||
#
|
||||
# Global switches
|
||||
#
|
||||
'
|
||||
|
||||
complete_from_list "-n '__fish_use_subcommand'" (cmd --help-options 2>| sed -n -e 's/'$re'/\1\t\t\2\t\4/p')
|
||||
|
||||
set cmd_str_internal (cmd --help-commands 2>| sed -n -e 's/^ *\([^ ][^ ]*\)[\t ]*\([^ ].*\)$/\1\t\2/p')
|
||||
for i in $cmd_str_internal
|
||||
set exploded (echo $i|tr \t \n)
|
||||
set cmd_str $cmd_str "-a $exploded[1] --description '"(esc $exploded[2])"'"
|
||||
end
|
||||
|
||||
case '*'
|
||||
|
||||
function list_subcommand
|
||||
cmd help | sed -n -e 's/^ *\([^ ][^ ]*\) .*$/\1/p'
|
||||
end
|
||||
|
||||
function list_subcommand_help
|
||||
set -l short_exp '\(-.\|\)\( [^ -][^ ]*\|\)'
|
||||
set -l long_exp '\(--[^ =,]*\)'
|
||||
set -l arg_exp '\(\|[= ][^ ][^ ]*\)'
|
||||
set -l desc_exp '\([\t ]*:[\t ]*\|\)\([^ ].*[^.]\)'
|
||||
set -l re "^ *$short_exp *$long_exp$arg_exp *$desc_exp\(\|\\.\)\$"
|
||||
|
||||
cmd help $argv | sed -n -e 's/'$re'/\1\t\3\t\4\t\6/p'
|
||||
end
|
||||
|
||||
set cmd_str (cmd help | sed -n -e 's/^ *\([^ ][^ ]*\)[\t ]*\([^ ].*[^.]\)\(\|\\.\)$/-a \1 --description \'\2\'/p')
|
||||
|
||||
end
|
||||
|
||||
echo '
|
||||
#
|
||||
# subcommands
|
||||
#
|
||||
'
|
||||
|
||||
printf "complete -c $cmd -n '__fish_use_subcommand' -x %s\n" $cmd_str
|
||||
|
||||
for i in (list_subcommand)
|
||||
|
||||
echo '
|
||||
|
||||
#
|
||||
# Completions for the \''$i'\' subcommand
|
||||
#
|
||||
'
|
||||
|
||||
complete_from_list "-n 'contains \\'$i\\' (commandline -poc)'" (list_subcommand_help $i)
|
||||
end
|
||||
|
||||
echo \n\n
|
||||
|
||||
end
|
||||
@@ -1,55 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Script to generate a tarball of vendored (downloaded) Rust dependencies
|
||||
# and the cargo configuration to ensure they are used
|
||||
# This tarball should be unpacked into a fish source directory
|
||||
# Outputs to $FISH_ARTEFACT_PATH or ~/fish_built by default
|
||||
|
||||
# Exit on error
|
||||
set -e
|
||||
|
||||
# We need GNU tar as that supports the --mtime and --transform options
|
||||
TAR=notfound
|
||||
for try in tar gtar gnutar; do
|
||||
if $try -Pcf /dev/null --mtime now /dev/null >/dev/null 2>&1; then
|
||||
TAR=$try
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$TAR" = "notfound" ]; then
|
||||
echo 'No suitable tar (supporting --mtime) found as tar/gtar/gnutar in PATH'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Get the current directory, which we'll use for telling Cargo where to find the sources
|
||||
wd="$PWD"
|
||||
|
||||
# Get the version from git-describe
|
||||
VERSION=$(build_tools/git_version_gen.sh --stdout 2>/dev/null)
|
||||
|
||||
# The name of the prefix, which is the directory that you get when you untar
|
||||
prefix="fish-$VERSION"
|
||||
|
||||
# The path where we will output the tar file
|
||||
# Defaults to ~/fish_built
|
||||
path=${FISH_ARTEFACT_PATH:-~/fish_built}/$prefix-vendor.tar
|
||||
|
||||
# Clean up stuff we've written before
|
||||
rm -f "$path" "$path".xz
|
||||
|
||||
# Work in a temporary directory to avoid clobbering the source directory
|
||||
PREFIX_TMPDIR=$(mktemp -d)
|
||||
cd "$PREFIX_TMPDIR"
|
||||
|
||||
mkdir .cargo
|
||||
cargo vendor --manifest-path "$wd/Cargo.toml" > .cargo/config.toml
|
||||
|
||||
tar cfvJ "$path".xz vendor .cargo
|
||||
|
||||
cd -
|
||||
rm -r "$PREFIX_TMPDIR"
|
||||
|
||||
# Output what we did, and the sha256 hash
|
||||
echo "Tarball written to $path".xz
|
||||
openssl dgst -sha256 "$path".xz
|
||||
@@ -1,11 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||
<installer-gui-script minSpecVersion="1">
|
||||
<title>fish shell</title>
|
||||
<welcome file="welcome.html" mime-type="text/html"/>
|
||||
<welcome file="welcome.rtf"/>
|
||||
<background file="terminal_logo.png" scaling="proportional" alignment="bottomleft"/>
|
||||
<pkg-ref id="com.ridiculousfish.fish-shell-pkg"/>
|
||||
<options hostArchitectures="arm64,x86_64" rootVolumeOnly="true"/>
|
||||
<options customize="never" require-scripts="true"/>
|
||||
<options customize="never" require-scripts="false"/>
|
||||
<choices-outline>
|
||||
<line choice="default">
|
||||
<line choice="com.ridiculousfish.fish-shell-pkg"/>
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 51 KiB |
@@ -1,28 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
body {
|
||||
font-family: system-ui, -apple-system, "Helvetica Neue", sans-serif;
|
||||
font-size: 10pt;
|
||||
}
|
||||
code, tt {
|
||||
font-family: ui-monospace, Menlo, monospace;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
<strong>fish</strong> is a smart and user-friendly command line shell. For more information, visit <a href="https://fishshell.com">fishshell.com</a>.
|
||||
</p>
|
||||
<p>
|
||||
<strong>fish</strong> will be installed into <tt>/usr/local/</tt>, and its path will be added to <wbr><tt>/etc/shells</tt> if necessary.
|
||||
</p>
|
||||
<p>
|
||||
Your default shell will <em>not</em> be changed. To make <strong>fish</strong> your login shell after the installation, run:
|
||||
</p>
|
||||
<p>
|
||||
<code>chsh -s /usr/local/bin/fish</code>
|
||||
</p>
|
||||
<p>Enjoy! Bugs can be reported on <a href="https://github.org/fish-shell/fish-shell/">GitHub</a>.</p>
|
||||
</body>
|
||||
</html>
|
||||
25
build_tools/osx_package_resources/welcome.rtf
Normal file
25
build_tools/osx_package_resources/welcome.rtf
Normal file
@@ -0,0 +1,25 @@
|
||||
{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370
|
||||
{\fonttbl\f0\fswiss\fcharset0 Helvetica;\f1\fnil\fcharset0 Monaco;}
|
||||
{\colortbl;\red255\green255\blue255;}
|
||||
{\info
|
||||
{\author dlkfjslfjsfdlkfk}}\margl1440\margr1440\vieww10800\viewh8400\viewkind0
|
||||
\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural
|
||||
|
||||
\f0\fs30 \cf0 The fish shell is a smart and user friendly command line shell. For more information, visit {\field{\*\fldinst{HYPERLINK "http://fishshell.com"}}{\fldrslt http://fishshell.com}}.\
|
||||
\
|
||||
fish will be installed into
|
||||
\f1\fs26 /usr/local/
|
||||
\f0\fs30 , and fish will be added to
|
||||
\f1\fs26 /etc/shells
|
||||
\f0\fs30 if necessary.\
|
||||
\
|
||||
Your default shell will
|
||||
\i not
|
||||
\i0 be changed. To make fish your default, run:\
|
||||
\
|
||||
|
||||
\f1 chsh -s /usr/local/bin/fish
|
||||
\f0 \
|
||||
\
|
||||
Enjoy!\
|
||||
}
|
||||
@@ -4,12 +4,12 @@
|
||||
|
||||
if test $# -eq 0
|
||||
then
|
||||
echo "usage: $0 shellname [shellname ...]"
|
||||
echo usage: $0 shellname [shellname ...]
|
||||
exit 1
|
||||
fi
|
||||
|
||||
scriptname=$(basename "$0")
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
scriptname=`basename "$0"`
|
||||
if [[ $UID -ne 0 ]]; then
|
||||
echo "${scriptname} must be run as root"
|
||||
exit 1
|
||||
fi
|
||||
@@ -20,7 +20,6 @@ tmpfile=${file}.tmp
|
||||
|
||||
set -o noclobber
|
||||
|
||||
# shellcheck disable=SC2064
|
||||
trap "rm -f $tmpfile" EXIT
|
||||
|
||||
if ! cat $file > $tmpfile
|
||||
@@ -33,13 +32,15 @@ EOF
|
||||
fi
|
||||
|
||||
# Append a newline if it doesn't exist
|
||||
[ -z "$(tail -c1 "$tmpfile")" ] || echo "" >> "$tmpfile"
|
||||
if [ "$(tail -c1 "$tmpfile"; echo x)" != $'\nx' ]; then
|
||||
echo "" >> "$tmpfile"
|
||||
fi
|
||||
|
||||
for i
|
||||
do
|
||||
if ! grep -q "^${i}$" "$tmpfile"
|
||||
then
|
||||
echo "$i" >> "$tmpfile"
|
||||
echo $i >> "$tmpfile"
|
||||
fi
|
||||
done
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh -x
|
||||
|
||||
./add-shell "${DSTVOLUME}"usr/local/bin/fish
|
||||
./add-shell /usr/local/bin/fish > /tmp/fish_postinstall_output.log
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
#!/bin/sh -x
|
||||
|
||||
echo "Removing any previous installation"
|
||||
pkgutil --pkg-info "${INSTALL_PKG_SESSION_ID}" && pkgutil --only-files --files "${INSTALL_PKG_SESSION_ID}" | while read -r installed
|
||||
do rm -v "${DSTVOLUME}${installed}"
|
||||
done
|
||||
echo "... removed"
|
||||
@@ -1,104 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
workspace_root=$(dirname "$0")/..
|
||||
|
||||
relnotes_tmp=$(mktemp -d)
|
||||
mkdir -p "$relnotes_tmp/fake-workspace" "$relnotes_tmp/out"
|
||||
(
|
||||
cd "$workspace_root"
|
||||
cp -r doc_src CONTRIBUTING.rst README.rst "$relnotes_tmp/fake-workspace"
|
||||
)
|
||||
version=$(sed 's,^fish \(\S*\) .*,\1,; 1q' "$workspace_root/CHANGELOG.rst")
|
||||
previous_version=$(
|
||||
cd "$workspace_root"
|
||||
awk <CHANGELOG.rst '
|
||||
( /^fish \S*\.\S*\.\S* \(released .*\)$/ &&
|
||||
NR > 1 &&
|
||||
# Skip tags that have not been created yet..
|
||||
system("git rev-parse --verify >/dev/null --quiet refs/tags/"$2) == 0 \
|
||||
) {
|
||||
print $2; ok = 1; exit
|
||||
}
|
||||
END { exit !ok }
|
||||
'
|
||||
)
|
||||
minor_version=${version%.*}
|
||||
previous_minor_version=${previous_version%.*}
|
||||
{
|
||||
sed -n 1,2p <"$workspace_root/CHANGELOG.rst"
|
||||
|
||||
ListCommitters() {
|
||||
comm "$@" "$relnotes_tmp/committers-then" "$relnotes_tmp/committers-now"
|
||||
}
|
||||
(
|
||||
cd "$workspace_root"
|
||||
git log "$previous_version" --format="%aN" | sort -u >"$relnotes_tmp/committers-then"
|
||||
git log "$previous_version".. --format="%aN" | sort -u >"$relnotes_tmp/committers-now"
|
||||
ListCommitters -13 >"$relnotes_tmp/committers-new"
|
||||
ListCommitters -12 >"$relnotes_tmp/committers-returning"
|
||||
)
|
||||
if [ "$minor_version" != "$previous_minor_version" ]; then
|
||||
(
|
||||
cd "$workspace_root"
|
||||
num_commits=$(git log --no-merges --format=%H "$previous_version".. | wc -l)
|
||||
num_authors=$(wc -l <"$relnotes_tmp/committers-now")
|
||||
num_new_authors=$(wc -l <"$relnotes_tmp/committers-new")
|
||||
printf %s \
|
||||
"This release comprises $num_commits commits since $previous_version," \
|
||||
" contributed by $num_authors authors, $num_new_authors of which are new committers."
|
||||
echo
|
||||
echo
|
||||
)
|
||||
fi
|
||||
|
||||
printf '%s\n' "$(awk <"$workspace_root/CHANGELOG.rst" '
|
||||
NR <= 2 || /^\.\. ignore / { next }
|
||||
/^===/ { exit }
|
||||
{ print }
|
||||
' | sed '$d')" |
|
||||
sed -e '$s/^----*$//' # Remove spurious transitions at the end of the document.
|
||||
|
||||
if [ "$minor_version" != "$previous_minor_version" ]; then {
|
||||
JoinEscaped() {
|
||||
sed 's/\S/\\&/g' |
|
||||
awk '
|
||||
NR != 1 { printf ",\n" }
|
||||
{ printf "%s", $0 }
|
||||
END { printf "\n" }
|
||||
'
|
||||
}
|
||||
echo ""
|
||||
echo "---"
|
||||
echo ""
|
||||
echo "Thanks to everyone who contributed through issue discussions, code reviews, or code changes."
|
||||
echo
|
||||
printf "Welcome our new committers: "
|
||||
JoinEscaped <"$relnotes_tmp/committers-new"
|
||||
echo
|
||||
printf "Welcome back our returning committers: "
|
||||
JoinEscaped <"$relnotes_tmp/committers-returning"
|
||||
} fi
|
||||
echo
|
||||
echo "---"
|
||||
echo
|
||||
echo "*Download links: To download the source code for fish, we suggest the file named \"fish-$version.tar.xz\". The file downloaded from \"Source code (tar.gz)\" will not build correctly.*"
|
||||
echo
|
||||
echo "*The files called fish-$version-linux-\*.tar.xz are experimental packages containing a single standalone ``fish`` binary for any Linux with the given CPU architecture.*"
|
||||
} >"$relnotes_tmp/fake-workspace"/CHANGELOG.rst
|
||||
|
||||
sphinx-build >&2 -j auto \
|
||||
-W -E -b markdown -c "$workspace_root/doc_src" \
|
||||
-d "$relnotes_tmp/doctree" "$relnotes_tmp/fake-workspace/doc_src" "$relnotes_tmp/out" \
|
||||
-D markdown_http_base="https://fishshell.com/docs/$minor_version" \
|
||||
-D markdown_uri_doc_suffix=".html" \
|
||||
-D markdown_github_flavored=1 \
|
||||
"$@"
|
||||
|
||||
# Skip changelog header
|
||||
sed -n 1p "$relnotes_tmp/out/relnotes.md" | grep -Fxq "# Release notes"
|
||||
sed -n 2p "$relnotes_tmp/out/relnotes.md" | grep -Fxq ''
|
||||
sed 1,2d "$relnotes_tmp/out/relnotes.md"
|
||||
|
||||
rm -r "$relnotes_tmp"
|
||||
@@ -1,253 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
{
|
||||
|
||||
set -ex
|
||||
|
||||
version=$1
|
||||
repository_owner=fish-shell
|
||||
remote=origin
|
||||
if [ -n "$2" ]; then
|
||||
set -u
|
||||
repository_owner=$2
|
||||
remote=$3
|
||||
set +u
|
||||
[ $# -eq 3 ]
|
||||
fi
|
||||
|
||||
[ -n "$version" ]
|
||||
|
||||
for tool in \
|
||||
bundle \
|
||||
gh \
|
||||
jq \
|
||||
ruby \
|
||||
timeout \
|
||||
; do
|
||||
if ! command -v "$tool" >/dev/null; then
|
||||
echo >&2 "$0: missing command: $1"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
repo_root="$(dirname "$0")/.."
|
||||
fish_site=$repo_root/../fish-site
|
||||
fish_site_repo=git@github.com:$repository_owner/fish-site
|
||||
|
||||
for path in . "$fish_site"
|
||||
do
|
||||
if ! git -C "$path" diff HEAD --quiet ||
|
||||
git ls-files --others --exclude-standard | grep .; then
|
||||
echo >&2 "$0: index and worktree must be clean"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
(
|
||||
cd "$fish_site"
|
||||
[ "$(git rev-parse HEAD)" = \
|
||||
"$(git ls-remote "$fish_site_repo" refs/heads/master |
|
||||
awk '{print $1}')" ]
|
||||
)
|
||||
|
||||
if git tag | grep -qxF "$version"; then
|
||||
echo >&2 "$0: tag $version already exists"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
integration_branch=$(
|
||||
git for-each-ref --points-at=HEAD 'refs/heads/Integration_*' \
|
||||
--format='%(refname:strip=2)'
|
||||
)
|
||||
[ -n "$integration_branch" ] ||
|
||||
git merge-base --is-ancestor $remote/master HEAD
|
||||
|
||||
sed -n 1p CHANGELOG.rst | grep -q '^fish .*(released .*)$'
|
||||
sed -n 2p CHANGELOG.rst | grep -q '^===*$'
|
||||
|
||||
changelog_title="fish $version (released $(date +'%B %d, %Y'))"
|
||||
sed -i \
|
||||
-e "1c$changelog_title" \
|
||||
-e "2c$(printf %s "$changelog_title" | sed s/./=/g)" \
|
||||
CHANGELOG.rst
|
||||
|
||||
CommitVersion() {
|
||||
sed -i "s/^version = \".*\"/version = \"$1\"/g" Cargo.toml
|
||||
cargo fetch --offline
|
||||
git add CHANGELOG.rst Cargo.toml Cargo.lock
|
||||
git commit -m "$2
|
||||
|
||||
Created by ./build_tools/release.sh $version"
|
||||
}
|
||||
|
||||
CommitVersion "$version" "Release $version"
|
||||
|
||||
# N.B. this is not GPG-signed.
|
||||
git tag --annotate --message="Release $version" $version
|
||||
|
||||
git push $remote $version
|
||||
|
||||
TIMEOUT=
|
||||
gh() {
|
||||
command ${TIMEOUT:+timeout $TIMEOUT} \
|
||||
gh --repo "$repository_owner/fish-shell" "$@"
|
||||
}
|
||||
|
||||
gh workflow run release.yml --ref="$version" \
|
||||
--raw-field="version=$version"
|
||||
|
||||
run_id=
|
||||
while [ -z "$run_id" ] && sleep 5
|
||||
do
|
||||
run_id=$(gh run list \
|
||||
--json=databaseId --jq=.[].databaseId \
|
||||
--workflow=release.yml --limit=1 \
|
||||
--commit="$(git rev-parse "$version^{commit}")")
|
||||
done
|
||||
|
||||
# Update fishshell.com
|
||||
tag_oid=$(git rev-parse "$version")
|
||||
tmpdir=$(mktemp -d)
|
||||
# TODO This works on draft releases only if "gh" is configured to
|
||||
# have write access to the fish-shell repository. Unless we are fine
|
||||
# publishing the release at this point, we should at least fail if
|
||||
# "gh" doesn't have write access.
|
||||
while ! \
|
||||
gh release download "$version" --dir="$tmpdir" \
|
||||
--pattern="fish-$version.tar.xz"
|
||||
do
|
||||
TIMEOUT=30 gh run watch "$run_id" ||:
|
||||
sleep 5
|
||||
done
|
||||
actual_tag_oid=$(git ls-remote "$remote" |
|
||||
awk '$2 == "refs/tags/'"$version"'" { print $1 }')
|
||||
[ "$tag_oid" = "$actual_tag_oid" ]
|
||||
( cd "$tmpdir" && tar xf fish-$version.tar.xz )
|
||||
CopyDocs() {
|
||||
rm -rf "$fish_site/site/docs/$1"
|
||||
cp -r "$tmpdir/fish-$version/user_doc/html" "$fish_site/site/docs/$1"
|
||||
git -C $fish_site add "site/docs/$1"
|
||||
}
|
||||
minor_version=${version%.*}
|
||||
CopyDocs "$minor_version"
|
||||
latest_release=$(
|
||||
releases=$(git tag | grep '^[0-9]*\.[0-9]*\.[0-9]*.*' |
|
||||
sed $(: "De-prioritize release candidates (1.2.3-rc0)") \
|
||||
's/-/~/g' | LC_ALL=C sort --version-sort)
|
||||
printf %s\\n "$releases" | tail -1
|
||||
)
|
||||
if [ "$version" = "$latest_release" ]; then
|
||||
CopyDocs current
|
||||
fi
|
||||
rm -rf "$tmpdir"
|
||||
(
|
||||
cd "$fish_site"
|
||||
make
|
||||
git add -u
|
||||
git add docs
|
||||
if git ls-files --others --exclude-standard | grep .; then
|
||||
exit 1
|
||||
fi
|
||||
git commit --message="$(printf %s "\
|
||||
| Release $version (docs)
|
||||
|
|
||||
| Created by ../fish-shell/build_tools/release.sh
|
||||
" | sed 's,^\s*| \?,,')"
|
||||
)
|
||||
|
||||
gh_api_repo() {
|
||||
path=$1
|
||||
shift
|
||||
command gh api \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||
"/repos/$repository_owner/fish-shell/$path" \
|
||||
"$@"
|
||||
}
|
||||
|
||||
# Approve macos-codesign
|
||||
# TODO what if current user can't approve?
|
||||
gh_pending_deployments() {
|
||||
gh_api_repo "actions/runs/$run_id/pending_deployments" "$@"
|
||||
}
|
||||
while {
|
||||
environment_id=$(gh_pending_deployments | jq .[].environment.id)
|
||||
[ -z "$environment_id" ]
|
||||
}
|
||||
do
|
||||
sleep 5
|
||||
done
|
||||
echo '
|
||||
{
|
||||
"environment_ids": ['"$environment_id"'],
|
||||
"state": "approved",
|
||||
"comment": "Approved via ./build_tools/release.sh"
|
||||
}
|
||||
' |
|
||||
gh_pending_deployments --method POST --input=-
|
||||
|
||||
# Await completion.
|
||||
gh run watch "$run_id"
|
||||
|
||||
while {
|
||||
! draft=$(gh release view "$version" --json=isDraft --jq=.isDraft) \
|
||||
|| [ "$draft" = true ]
|
||||
}
|
||||
do
|
||||
sleep 20
|
||||
done
|
||||
|
||||
(
|
||||
cd "$fish_site"
|
||||
make new-release
|
||||
git add -u
|
||||
git add docs
|
||||
if git ls-files --others --exclude-standard | grep .; then
|
||||
exit 1
|
||||
fi
|
||||
git commit --message="$(printf %s "\
|
||||
| Release $version (release list update)
|
||||
|
|
||||
| Created by ../fish-shell/build_tools/release.sh
|
||||
" | sed 's,^\s*| \?,,')"
|
||||
# This takes care to support remote names that are different from
|
||||
# fish-shell remote name. Also, support detached HEAD state.
|
||||
git push "$fish_site_repo" HEAD:master
|
||||
)
|
||||
|
||||
if [ -n "$integration_branch" ]; then {
|
||||
git push $remote "$version^{commit}":refs/heads/$integration_branch
|
||||
} else {
|
||||
changelog=$(cat - CHANGELOG.rst <<EOF
|
||||
fish ?.?.? (released ???)
|
||||
=========================
|
||||
|
||||
EOF
|
||||
)
|
||||
printf %s\\n "$changelog" >CHANGELOG.rst
|
||||
CommitVersion ${version}-snapshot "start new cycle"
|
||||
git push $remote HEAD:master
|
||||
} fi
|
||||
|
||||
milestone_number=$(
|
||||
gh_api_repo milestones?state=open |
|
||||
jq '.[] | select(.title == "fish '"$version"'") | .number'
|
||||
)
|
||||
gh_api_repo --method PATCH milestones/$milestone_number \
|
||||
--raw-field state=closed
|
||||
|
||||
next_patch_version=$(
|
||||
echo "$version" | awk -F. '
|
||||
NF == 3 && $3 ~ /[0-9]+/ {
|
||||
printf "%s.%s.%s", $1, $2, $3+1
|
||||
}
|
||||
'
|
||||
)
|
||||
if [ -n "$next_patch_version" ]; then
|
||||
gh_api_repo --method POST milestones \
|
||||
--raw-field title="fish $next_patch_version"
|
||||
fi
|
||||
|
||||
exit
|
||||
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
#!/usr/bin/env fish
|
||||
#
|
||||
# This runs Python files, fish scripts (*.fish), and Rust files
|
||||
# through their respective code formatting programs.
|
||||
#
|
||||
# `--all`: Format all eligible files instead of the ones specified as arguments.
|
||||
# `--check`: Instead of reformatting, fail if a file is not formatted correctly.
|
||||
# `--force`: Proceed without asking if uncommitted changes are detected.
|
||||
# Only relevant if `--all` is specified but `--check` is not specified.
|
||||
|
||||
set -l fish_files
|
||||
set -l python_files
|
||||
set -l rust_files
|
||||
set -l all no
|
||||
|
||||
argparse all check force -- $argv
|
||||
or exit $status
|
||||
|
||||
if set -l -q _flag_all
|
||||
set all yes
|
||||
if set -q argv[1]
|
||||
echo "Unexpected arguments: '$argv'"
|
||||
exit 1
|
||||
end
|
||||
end
|
||||
|
||||
set -l workspace_root (status dirname)/..
|
||||
|
||||
if test $all = yes
|
||||
if not set -l -q _flag_force; and not set -l -q _flag_check
|
||||
# Potential for false positives: Not all fish files are formatted, see the `fish_files`
|
||||
# definition below.
|
||||
set -l relevant_uncommitted_changes (git status --porcelain --short --untracked-files=all | sed -e 's/^ *[^ ]* *//' | grep -E '.*\.(fish|py|rs)$')
|
||||
if set -q relevant_uncommitted_changes[1]
|
||||
for changed_file in $relevant_uncommitted_changes
|
||||
echo $changed_file
|
||||
end
|
||||
echo
|
||||
echo 'You have uncommitted changes (listed above). Are you sure you want to restyle?'
|
||||
read -P 'y/N? ' -n1 -l ans
|
||||
if not string match -qi y -- $ans
|
||||
exit 1
|
||||
end
|
||||
end
|
||||
end
|
||||
set fish_files $workspace_root/{benchmarks,build_tools,etc,share}/**.fish
|
||||
set python_files {doc_src,share,tests}/**.py
|
||||
else
|
||||
# Format the files specified as arguments.
|
||||
set -l files $argv
|
||||
set fish_files (string match -r '^.*\.fish$' -- $files)
|
||||
set python_files (string match -r '^.*\.py$' -- $files)
|
||||
set rust_files (string match -r '^.*\.rs$' -- $files)
|
||||
end
|
||||
|
||||
set -l red (set_color red)
|
||||
set -l green (set_color green)
|
||||
set -l yellow (set_color yellow)
|
||||
set -l normal (set_color normal)
|
||||
|
||||
if set -q fish_files[1]
|
||||
if not type -q fish_indent
|
||||
echo
|
||||
echo $yellow'Could not find `fish_indent` in `$PATH`.'$normal
|
||||
exit 127
|
||||
end
|
||||
echo === Running "$green"fish_indent"$normal"
|
||||
if set -l -q _flag_check
|
||||
if not fish_indent --check -- $fish_files
|
||||
echo $red"Fish files are not formatted correctly."$normal
|
||||
exit 1
|
||||
end
|
||||
else
|
||||
fish_indent -w -- $fish_files
|
||||
end
|
||||
end
|
||||
|
||||
if set -q python_files[1]
|
||||
if not type -q black
|
||||
echo
|
||||
echo $yellow'Please install `black` to style python'$normal
|
||||
exit 127
|
||||
end
|
||||
echo === Running "$green"black"$normal"
|
||||
if set -l -q _flag_check
|
||||
if not black --check $python_files
|
||||
echo $red"Python files are not formatted correctly."$normal
|
||||
exit 1
|
||||
end
|
||||
else
|
||||
black $python_files
|
||||
end
|
||||
end
|
||||
|
||||
if not cargo fmt --version >/dev/null
|
||||
echo
|
||||
echo $yellow'Please install "rustfmt" to style Rust, e.g. via:'
|
||||
echo "rustup component add rustfmt"$normal
|
||||
exit 127
|
||||
end
|
||||
echo === Running "$green"rustfmt"$normal"
|
||||
if set -l -q _flag_check
|
||||
if set -l -q _flag_all
|
||||
if not cargo fmt --check
|
||||
echo $red"Rust files are not formatted correctly."$normal
|
||||
exit 1
|
||||
end
|
||||
else
|
||||
if set -q rust_files[1]
|
||||
if not rustfmt --check --files-with-diff $rust_files
|
||||
echo $red"Rust files are not formatted correctly."
|
||||
exit 1
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
if set -l -q _flag_all
|
||||
cargo fmt
|
||||
else
|
||||
if set -q rust_files[1]
|
||||
rustfmt $rust_files
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,165 +0,0 @@
|
||||
#!/usr/bin/env fish
|
||||
|
||||
# Updates the files used for gettext translations.
|
||||
# By default, the whole xgettext + msgmerge pipeline runs,
|
||||
# which extracts the messages from the source files into $template_file,
|
||||
# and updates the PO files for each language from that.
|
||||
#
|
||||
# Use cases:
|
||||
# For developers:
|
||||
# - Run with no args to update all PO files after making changes to Rust/fish sources.
|
||||
# For translators:
|
||||
# - Specify the language you want to work on as an argument, which must be a file in the po/
|
||||
# directory. You can specify a language which does not have translations yet by specifying the
|
||||
# name of a file which does not yet exist. Make sure to follow the naming convention.
|
||||
# For testing:
|
||||
# - Specify `--dry-run` to see if any updates to the PO files would by applied by this script.
|
||||
# If this flag is specified, the script will exit with an error if there are outstanding
|
||||
# changes, and will display the diff. Do not specify other flags if `--dry-run` is specified.
|
||||
#
|
||||
# Specify `--use-existing-template=FILE` to prevent running cargo for extracting an up-to-date
|
||||
# version of the localized strings. This flag is intended for testing setups which make it
|
||||
# inconvenient to run cargo here, but run it in an earlier step to ensure up-to-date values.
|
||||
# This argument is passed on to the `fish_xgettext.fish` script and has no other uses.
|
||||
# `FILE` must be the path to a gettext template file generated from our compilation process.
|
||||
# It can be obtained by running:
|
||||
# set -l FILE (mktemp)
|
||||
# FISH_GETTEXT_EXTRACTION_FILE=$FILE cargo check --features=gettext-extract
|
||||
|
||||
# The sort utility is locale-sensitive.
|
||||
# Ensure that sorting output is consistent by setting LC_ALL here.
|
||||
set -gx LC_ALL C.UTF-8
|
||||
|
||||
set -l build_tools (status dirname)
|
||||
set -g tmpdir
|
||||
set -l po_dir $build_tools/../po
|
||||
|
||||
set -l extract
|
||||
set -l po
|
||||
|
||||
argparse dry-run use-existing-template= -- $argv
|
||||
or exit $status
|
||||
|
||||
if test -z $argv[1]
|
||||
# Update everything if not specified otherwise.
|
||||
set -g po_files $po_dir/*.po
|
||||
else
|
||||
set -l po_dir_id (stat --format='%d:%i' -- $po_dir)
|
||||
for arg in $argv
|
||||
set -l arg_dir_id (stat --format='%d:%i' -- (dirname $arg))
|
||||
if test $po_dir_id != $arg_dir_id
|
||||
echo "Argument $arg is not a file in the directory $(realpath $po_dir)."
|
||||
echo "Non-option arguments must specify paths to files in this directory."
|
||||
echo ""
|
||||
echo "If you want to add a new language to the translations not the following:"
|
||||
echo "The filename must identify a language, with a two letter ISO 639-1 language code of the target language (e.g. 'pt' for Portuguese), and use the file extension '.po'."
|
||||
echo "Optionally, you can specify a regional variant (e.g. 'pt_BR')."
|
||||
echo "So valid filenames are of the shape 'll.po' or 'll_CC.po'."
|
||||
exit 1
|
||||
end
|
||||
if not basename $arg | grep -qE '^[a-z]{2,3}(_[A-Z]{2})?\.po$'
|
||||
echo "Filename does not match the expected format ('ll.po' or 'll_CC.po')."
|
||||
exit 1
|
||||
end
|
||||
end
|
||||
set -g po_files $argv
|
||||
end
|
||||
|
||||
set -g template_file (mktemp)
|
||||
# Protect from externally set $tmpdir leaking into this script.
|
||||
set -g tmpdir
|
||||
|
||||
function cleanup_exit
|
||||
set -l exit_status $status
|
||||
|
||||
rm $template_file
|
||||
|
||||
if set -g --query tmpdir[1]
|
||||
rm -r $tmpdir
|
||||
end
|
||||
|
||||
exit $exit_status
|
||||
end
|
||||
|
||||
if set -l --query extract
|
||||
set -l xgettext_args
|
||||
if set -l --query _flag_use_existing_template
|
||||
set xgettext_args --use-existing-template=$_flag_use_existing_template
|
||||
end
|
||||
$build_tools/fish_xgettext.fish $xgettext_args >$template_file
|
||||
or cleanup_exit
|
||||
end
|
||||
|
||||
if set -l --query _flag_dry_run
|
||||
# On a dry run, we do not modify po/ but write to a temporary directory instead and check if
|
||||
# there is a difference between po/ and the tmpdir after re-generating the PO files.
|
||||
set -g tmpdir (mktemp -d)
|
||||
|
||||
# Ensure tmpdir has the same initial state as the po dir.
|
||||
cp -r $po_dir/* $tmpdir
|
||||
end
|
||||
|
||||
function merge_po_files --argument-names template_file po_file
|
||||
msgmerge --no-wrap --update --no-fuzzy-matching --backup=none --quiet \
|
||||
$po_file $template_file
|
||||
or cleanup_exit
|
||||
set -l new_po_file (mktemp) # TODO Remove on failure.
|
||||
and msgattrib --no-wrap --no-obsolete -o $new_po_file $po_file
|
||||
or cleanup_exit
|
||||
|
||||
begin
|
||||
echo "# fish-note-sections: Translations are divided into sections, each starting with a fish-section-* comment."
|
||||
echo "# fish-note-sections: The first few sections are more important."
|
||||
echo "# fish-note-sections: Ignore the tier3 sections unless you have a lot of time."
|
||||
sed -i '
|
||||
/^# fish-note-sections:/d;
|
||||
/^# fish-section-/d;
|
||||
' $new_po_file
|
||||
|
||||
set -l next_line 1
|
||||
set -l section
|
||||
awk <$template_file '
|
||||
/^# fish-section-\S*$/ {
|
||||
section = $0
|
||||
}
|
||||
section != "" && /^msgid ".+"$/ {
|
||||
print section
|
||||
print $0
|
||||
section = ""
|
||||
}
|
||||
' |
|
||||
while read -l section
|
||||
read -l msgid_line
|
||||
set -l line_number (grep -m1 -Fxn $msgid_line $new_po_file | string split :)[1]
|
||||
sed -n "$next_line,$(math $line_number - 1)"p $new_po_file
|
||||
echo $section
|
||||
set next_line $line_number
|
||||
# set section
|
||||
end
|
||||
sed -n "$next_line,\$"p $new_po_file
|
||||
end >$po_file
|
||||
rm $new_po_file
|
||||
end
|
||||
|
||||
for po_file in $po_files
|
||||
if set --query tmpdir[1]
|
||||
set po_file $tmpdir/(basename $po_file)
|
||||
end
|
||||
if set -l --query po
|
||||
if test -e $po_file
|
||||
merge_po_files $template_file $po_file
|
||||
else
|
||||
cp $template_file $po_file
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if set -g --query tmpdir[1]
|
||||
diff -ur $po_dir $tmpdir
|
||||
or begin
|
||||
echo ERROR: translations in ./po/ are stale. Try running build_tools/update_translations.fish
|
||||
cleanup_exit
|
||||
end
|
||||
end
|
||||
|
||||
cleanup_exit
|
||||
4147
builtin.cpp
Normal file
4147
builtin.cpp
Normal file
File diff suppressed because it is too large
Load Diff
182
builtin.h
Normal file
182
builtin.h
Normal file
@@ -0,0 +1,182 @@
|
||||
/** \file builtin.h
|
||||
Prototypes for functions for executing builtin functions.
|
||||
*/
|
||||
|
||||
#ifndef FISH_BUILTIN_H
|
||||
#define FISH_BUILTIN_H
|
||||
|
||||
#include <wchar.h>
|
||||
|
||||
#include "util.h"
|
||||
#include "io.h"
|
||||
#include "common.h"
|
||||
|
||||
class parser_t;
|
||||
|
||||
enum
|
||||
{
|
||||
COMMAND_NOT_BUILTIN,
|
||||
BUILTIN_REGULAR,
|
||||
BUILTIN_FUNCTION
|
||||
}
|
||||
;
|
||||
|
||||
/**
|
||||
Error message on missing argument
|
||||
*/
|
||||
#define BUILTIN_ERR_MISSING _( L"%ls: Expected argument\n" )
|
||||
|
||||
/**
|
||||
Error message on invalid combination of options
|
||||
*/
|
||||
#define BUILTIN_ERR_COMBO _( L"%ls: Invalid combination of options\n" )
|
||||
|
||||
/**
|
||||
Error message on invalid combination of options
|
||||
*/
|
||||
#define BUILTIN_ERR_COMBO2 _( L"%ls: Invalid combination of options,\n%ls\n" )
|
||||
|
||||
/**
|
||||
Error message on multiple scope levels for variables
|
||||
*/
|
||||
#define BUILTIN_ERR_GLOCAL _( L"%ls: Variable scope can only be one of universal, global and local\n" )
|
||||
|
||||
/**
|
||||
Error message for specifying both export and unexport to set/read
|
||||
*/
|
||||
#define BUILTIN_ERR_EXPUNEXP _( L"%ls: Variable can't be both exported and unexported\n" )
|
||||
|
||||
/**
|
||||
Error message for unknown switch
|
||||
*/
|
||||
#define BUILTIN_ERR_UNKNOWN _( L"%ls: Unknown option '%ls'\n" )
|
||||
|
||||
/**
|
||||
Error message for invalid character in variable name
|
||||
*/
|
||||
#define BUILTIN_ERR_VARCHAR _( L"%ls: Invalid character '%lc' in variable name. Only alphanumerical characters and underscores are valid in a variable name.\n" )
|
||||
|
||||
/**
|
||||
Error message for invalid (empty) variable name
|
||||
*/
|
||||
#define BUILTIN_ERR_VARNAME_ZERO _( L"%ls: Variable name can not be the empty string\n" )
|
||||
|
||||
/**
|
||||
Error message when second argument to for isn't 'in'
|
||||
*/
|
||||
#define BUILTIN_FOR_ERR_IN _( L"%ls: Second argument must be 'in'\n" )
|
||||
|
||||
/**
|
||||
Error message for insufficient number of arguments
|
||||
*/
|
||||
#define BUILTIN_FOR_ERR_COUNT _( L"%ls: Expected at least two arguments, got %d\n")
|
||||
|
||||
#define BUILTIN_FOR_ERR_NAME _( L"%ls: '%ls' is not a valid variable name\n" )
|
||||
|
||||
/** Error messages for 'else if' */
|
||||
#define BUILTIN_ELSEIF_ERR_COUNT _( L"%ls: can only take 'if' and then another command as an argument\n")
|
||||
#define BUILTIN_ELSEIF_ERR_ARGUMENT _( L"%ls: any second argument must be 'if'\n")
|
||||
|
||||
/**
|
||||
Error message when too many arguments are supplied to a builtin
|
||||
*/
|
||||
#define BUILTIN_ERR_TOO_MANY_ARGUMENTS _( L"%ls: Too many arguments\n" )
|
||||
|
||||
/**
|
||||
Error message when block types mismatch in the end builtin, e.g. 'begin; end for'
|
||||
*/
|
||||
#define BUILTIN_END_BLOCK_MISMATCH _( L"%ls: Block mismatch: '%ls' vs. '%ls'\n" )
|
||||
|
||||
/**
|
||||
Error message for unknown block type in the end builtin, e.g. 'begin; end beggin'
|
||||
*/
|
||||
#define BUILTIN_END_BLOCK_UNKNOWN _( L"%ls: Unknown block type '%ls'\n" )
|
||||
|
||||
#define BUILTIN_ERR_NOT_NUMBER _( L"%ls: Argument '%ls' is not a number\n" )
|
||||
|
||||
/** Get the string used to represent stdout and stderr */
|
||||
const wcstring &get_stdout_buffer();
|
||||
const wcstring &get_stderr_buffer();
|
||||
|
||||
/** Output an error */
|
||||
void builtin_show_error(const wcstring &err);
|
||||
|
||||
/**
|
||||
Kludge. Tells builtins if output is to screen
|
||||
*/
|
||||
extern int builtin_out_redirect;
|
||||
|
||||
/**
|
||||
Kludge. Tells builtins if error is to screen
|
||||
*/
|
||||
extern int builtin_err_redirect;
|
||||
|
||||
|
||||
/**
|
||||
Initialize builtin data.
|
||||
*/
|
||||
void builtin_init();
|
||||
|
||||
/**
|
||||
Destroy builtin data.
|
||||
*/
|
||||
void builtin_destroy();
|
||||
|
||||
/**
|
||||
Is there a builtin command with the given name?
|
||||
*/
|
||||
int builtin_exists(const wcstring &cmd);
|
||||
|
||||
/**
|
||||
Execute a builtin command
|
||||
|
||||
\param parser The parser being used
|
||||
\param argv Array containing the command and parameters
|
||||
of the builtin. The list is terminated by a
|
||||
null pointer. This syntax resembles the syntax
|
||||
for exec.
|
||||
\param io the io redirections to perform on this builtin.
|
||||
|
||||
\return the exit status of the builtin command
|
||||
*/
|
||||
int builtin_run(parser_t &parser, const wchar_t * const *argv, const io_chain_t &io);
|
||||
|
||||
/** Returns a list of all builtin names */
|
||||
wcstring_list_t builtin_get_names(void);
|
||||
|
||||
/** Insert all builtin names into list. */
|
||||
void builtin_get_names(std::vector<completion_t> &list);
|
||||
|
||||
/**
|
||||
Pushes a new set of input/output to the stack. The new stdin is supplied, a new set of output strings is created.
|
||||
*/
|
||||
void builtin_push_io(parser_t &parser, int stdin_fd);
|
||||
|
||||
/**
|
||||
Pops a set of input/output from the stack. The output strings are destroued, but the input file is not closed.
|
||||
*/
|
||||
void builtin_pop_io(parser_t &parser);
|
||||
|
||||
|
||||
/**
|
||||
Return a one-line description of the specified builtin.
|
||||
*/
|
||||
wcstring builtin_get_desc(const wcstring &b);
|
||||
|
||||
|
||||
/**
|
||||
Slightly kludgy function used with 'complete -C' in order to make
|
||||
the commandline builtin operate on the string to complete instead
|
||||
of operating on whatever is to be completed.
|
||||
*/
|
||||
const wchar_t *builtin_complete_get_temporary_buffer();
|
||||
|
||||
|
||||
/**
|
||||
Run the __fish_print_help function to obtain the help information
|
||||
for the specified command.
|
||||
*/
|
||||
|
||||
wcstring builtin_help_get(parser_t &parser, const wchar_t *cmd);
|
||||
|
||||
#endif
|
||||
642
builtin_commandline.cpp
Normal file
642
builtin_commandline.cpp
Normal file
@@ -0,0 +1,642 @@
|
||||
/** \file builtin_commandline.c Functions defining the commandline builtin
|
||||
|
||||
Functions used for implementing the commandline builtin.
|
||||
|
||||
*/
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <wchar.h>
|
||||
#include <wctype.h>
|
||||
#include <sys/types.h>
|
||||
#include <termios.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include "fallback.h"
|
||||
#include "util.h"
|
||||
|
||||
#include "wutil.h"
|
||||
#include "builtin.h"
|
||||
#include "common.h"
|
||||
#include "wgetopt.h"
|
||||
#include "reader.h"
|
||||
#include "proc.h"
|
||||
#include "parser.h"
|
||||
#include "tokenizer.h"
|
||||
#include "input_common.h"
|
||||
#include "input.h"
|
||||
|
||||
#include "parse_util.h"
|
||||
|
||||
/**
|
||||
Which part of the comandbuffer are we operating on
|
||||
*/
|
||||
enum
|
||||
{
|
||||
STRING_MODE=1, /**< Operate on entire buffer */
|
||||
JOB_MODE, /**< Operate on job under cursor */
|
||||
PROCESS_MODE, /**< Operate on process under cursor */
|
||||
TOKEN_MODE /**< Operate on token under cursor */
|
||||
}
|
||||
;
|
||||
|
||||
/**
|
||||
For text insertion, how should it be done
|
||||
*/
|
||||
enum
|
||||
{
|
||||
REPLACE_MODE=1, /**< Replace current text */
|
||||
INSERT_MODE, /**< Insert at cursor position */
|
||||
APPEND_MODE /**< Insert at end of current token/command/buffer */
|
||||
}
|
||||
;
|
||||
|
||||
/**
|
||||
Pointer to what the commandline builtin considers to be the current
|
||||
contents of the command line buffer.
|
||||
*/
|
||||
static const wchar_t *current_buffer=0;
|
||||
/**
|
||||
What the commandline builtin considers to be the current cursor
|
||||
position.
|
||||
*/
|
||||
static size_t current_cursor_pos = (size_t)(-1);
|
||||
|
||||
/**
|
||||
Returns the current commandline buffer.
|
||||
*/
|
||||
static const wchar_t *get_buffer()
|
||||
{
|
||||
return current_buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the position of the cursor
|
||||
*/
|
||||
static size_t get_cursor_pos()
|
||||
{
|
||||
return current_cursor_pos;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Replace/append/insert the selection with/at/after the specified string.
|
||||
|
||||
\param begin beginning of selection
|
||||
\param end end of selection
|
||||
\param insert the string to insert
|
||||
\param append_mode can be one of REPLACE_MODE, INSERT_MODE or APPEND_MODE, affects the way the test update is performed
|
||||
*/
|
||||
static void replace_part(const wchar_t *begin,
|
||||
const wchar_t *end,
|
||||
const wchar_t *insert,
|
||||
int append_mode)
|
||||
{
|
||||
const wchar_t *buff = get_buffer();
|
||||
size_t out_pos = get_cursor_pos();
|
||||
|
||||
wcstring out;
|
||||
|
||||
out.append(buff, begin - buff);
|
||||
|
||||
switch (append_mode)
|
||||
{
|
||||
case REPLACE_MODE:
|
||||
{
|
||||
|
||||
out.append(insert);
|
||||
out_pos = wcslen(insert) + (begin-buff);
|
||||
break;
|
||||
|
||||
}
|
||||
case APPEND_MODE:
|
||||
{
|
||||
out.append(begin, end-begin);
|
||||
out.append(insert);
|
||||
break;
|
||||
}
|
||||
case INSERT_MODE:
|
||||
{
|
||||
long cursor = get_cursor_pos() -(begin-buff);
|
||||
out.append(begin, cursor);
|
||||
out.append(insert);
|
||||
out.append(begin+cursor, end-begin-cursor);
|
||||
out_pos += wcslen(insert);
|
||||
break;
|
||||
}
|
||||
}
|
||||
out.append(end);
|
||||
reader_set_buffer(out, out_pos);
|
||||
}
|
||||
|
||||
/**
|
||||
Output the specified selection.
|
||||
|
||||
\param begin start of selection
|
||||
\param end end of selection
|
||||
\param cut_at_cursor whether printing should stop at the surrent cursor position
|
||||
\param tokenize whether the string should be tokenized, printing one string token on every line and skipping non-string tokens
|
||||
*/
|
||||
static void write_part(const wchar_t *begin,
|
||||
const wchar_t *end,
|
||||
int cut_at_cursor,
|
||||
int tokenize)
|
||||
{
|
||||
wcstring out;
|
||||
wchar_t *buff;
|
||||
size_t pos;
|
||||
|
||||
pos = get_cursor_pos()-(begin-get_buffer());
|
||||
|
||||
if (tokenize)
|
||||
{
|
||||
buff = wcsndup(begin, end-begin);
|
||||
// fwprintf( stderr, L"Subshell: %ls, end char %lc\n", buff, *end );
|
||||
out.clear();
|
||||
tokenizer_t tok(buff, TOK_ACCEPT_UNFINISHED);
|
||||
for (; tok_has_next(&tok); tok_next(&tok))
|
||||
{
|
||||
if ((cut_at_cursor) &&
|
||||
(tok_get_pos(&tok)+wcslen(tok_last(&tok)) >= pos))
|
||||
break;
|
||||
|
||||
switch (tok_last_type(&tok))
|
||||
{
|
||||
case TOK_STRING:
|
||||
{
|
||||
out.append(escape_string(tok_last(&tok), UNESCAPE_INCOMPLETE));
|
||||
out.push_back(L'\n');
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
stdout_buffer.append(out);
|
||||
|
||||
free(buff);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (cut_at_cursor)
|
||||
{
|
||||
end = begin+pos;
|
||||
}
|
||||
|
||||
// debug( 0, L"woot2 %ls -> %ls", buff, esc );
|
||||
|
||||
stdout_buffer.append(begin, end - begin);
|
||||
stdout_buffer.append(L"\n");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
The commandline builtin. It is used for specifying a new value for
|
||||
the commandline.
|
||||
*/
|
||||
static int builtin_commandline(parser_t &parser, wchar_t **argv)
|
||||
{
|
||||
|
||||
int buffer_part=0;
|
||||
int cut_at_cursor=0;
|
||||
|
||||
int argc = builtin_count_args(argv);
|
||||
int append_mode=0;
|
||||
|
||||
int function_mode = 0;
|
||||
|
||||
int tokenize = 0;
|
||||
|
||||
int cursor_mode = 0;
|
||||
int line_mode = 0;
|
||||
int search_mode = 0;
|
||||
const wchar_t *begin, *end;
|
||||
|
||||
current_buffer = (wchar_t *)builtin_complete_get_temporary_buffer();
|
||||
if (current_buffer)
|
||||
{
|
||||
current_cursor_pos = wcslen(current_buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
current_buffer = reader_get_buffer();
|
||||
current_cursor_pos = reader_get_cursor_pos();
|
||||
}
|
||||
|
||||
if (!get_buffer())
|
||||
{
|
||||
if (is_interactive_session)
|
||||
{
|
||||
/*
|
||||
Prompt change requested while we don't have
|
||||
a prompt, most probably while reading the
|
||||
init files. Just ignore it.
|
||||
*/
|
||||
return 1;
|
||||
}
|
||||
|
||||
stderr_buffer.append(argv[0]);
|
||||
stderr_buffer.append(L": Can not set commandline in non-interactive mode\n");
|
||||
builtin_print_help(parser, argv[0], stderr_buffer);
|
||||
return 1;
|
||||
}
|
||||
|
||||
woptind=0;
|
||||
|
||||
while (1)
|
||||
{
|
||||
static const struct woption
|
||||
long_options[] =
|
||||
{
|
||||
{
|
||||
L"append", no_argument, 0, 'a'
|
||||
}
|
||||
,
|
||||
{
|
||||
L"insert", no_argument, 0, 'i'
|
||||
}
|
||||
,
|
||||
{
|
||||
L"replace", no_argument, 0, 'r'
|
||||
}
|
||||
,
|
||||
{
|
||||
L"current-job", no_argument, 0, 'j'
|
||||
}
|
||||
,
|
||||
{
|
||||
L"current-process", no_argument, 0, 'p'
|
||||
}
|
||||
,
|
||||
{
|
||||
L"current-token", no_argument, 0, 't'
|
||||
}
|
||||
,
|
||||
{
|
||||
L"current-buffer", no_argument, 0, 'b'
|
||||
}
|
||||
,
|
||||
{
|
||||
L"cut-at-cursor", no_argument, 0, 'c'
|
||||
}
|
||||
,
|
||||
{
|
||||
L"function", no_argument, 0, 'f'
|
||||
}
|
||||
,
|
||||
{
|
||||
L"tokenize", no_argument, 0, 'o'
|
||||
}
|
||||
,
|
||||
{
|
||||
L"help", no_argument, 0, 'h'
|
||||
}
|
||||
,
|
||||
{
|
||||
L"input", required_argument, 0, 'I'
|
||||
}
|
||||
,
|
||||
{
|
||||
L"cursor", no_argument, 0, 'C'
|
||||
}
|
||||
,
|
||||
{
|
||||
L"line", no_argument, 0, 'L'
|
||||
}
|
||||
,
|
||||
{
|
||||
L"search-mode", no_argument, 0, 'S'
|
||||
}
|
||||
,
|
||||
{
|
||||
0, 0, 0, 0
|
||||
}
|
||||
}
|
||||
;
|
||||
|
||||
int opt_index = 0;
|
||||
|
||||
int opt = wgetopt_long(argc,
|
||||
argv,
|
||||
L"abijpctwforhI:CLS",
|
||||
long_options,
|
||||
&opt_index);
|
||||
if (opt == -1)
|
||||
break;
|
||||
|
||||
switch (opt)
|
||||
{
|
||||
case 0:
|
||||
if (long_options[opt_index].flag != 0)
|
||||
break;
|
||||
append_format(stderr_buffer,
|
||||
BUILTIN_ERR_UNKNOWN,
|
||||
argv[0],
|
||||
long_options[opt_index].name);
|
||||
builtin_print_help(parser, argv[0], stderr_buffer);
|
||||
|
||||
return 1;
|
||||
|
||||
case L'a':
|
||||
append_mode = APPEND_MODE;
|
||||
break;
|
||||
|
||||
case L'b':
|
||||
buffer_part = STRING_MODE;
|
||||
break;
|
||||
|
||||
|
||||
case L'i':
|
||||
append_mode = INSERT_MODE;
|
||||
break;
|
||||
|
||||
case L'r':
|
||||
append_mode = REPLACE_MODE;
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
cut_at_cursor=1;
|
||||
break;
|
||||
|
||||
case 't':
|
||||
buffer_part = TOKEN_MODE;
|
||||
break;
|
||||
|
||||
case 'j':
|
||||
buffer_part = JOB_MODE;
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
buffer_part = PROCESS_MODE;
|
||||
break;
|
||||
|
||||
case 'f':
|
||||
function_mode=1;
|
||||
break;
|
||||
|
||||
case 'o':
|
||||
tokenize=1;
|
||||
break;
|
||||
|
||||
case 'I':
|
||||
current_buffer = woptarg;
|
||||
current_cursor_pos = wcslen(woptarg);
|
||||
break;
|
||||
|
||||
case 'C':
|
||||
cursor_mode = 1;
|
||||
break;
|
||||
|
||||
case 'L':
|
||||
line_mode = 1;
|
||||
break;
|
||||
|
||||
case 'S':
|
||||
search_mode = 1;
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
builtin_print_help(parser, argv[0], stdout_buffer);
|
||||
return 0;
|
||||
|
||||
case L'?':
|
||||
builtin_unknown_option(parser, argv[0], argv[woptind-1]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (function_mode)
|
||||
{
|
||||
int i;
|
||||
|
||||
/*
|
||||
Check for invalid switch combinations
|
||||
*/
|
||||
if (buffer_part || cut_at_cursor || append_mode || tokenize || cursor_mode || line_mode || search_mode)
|
||||
{
|
||||
append_format(stderr_buffer,
|
||||
BUILTIN_ERR_COMBO,
|
||||
argv[0]);
|
||||
|
||||
builtin_print_help(parser, argv[0], stderr_buffer);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
if (argc == woptind)
|
||||
{
|
||||
append_format(stderr_buffer,
|
||||
BUILTIN_ERR_MISSING,
|
||||
argv[0]);
|
||||
|
||||
builtin_print_help(parser, argv[0], stderr_buffer);
|
||||
return 1;
|
||||
}
|
||||
for (i=woptind; i<argc; i++)
|
||||
{
|
||||
wchar_t c = input_function_get_code(argv[i]);
|
||||
if (c != (wchar_t)(-1))
|
||||
{
|
||||
/*
|
||||
input_unreadch inserts the specified keypress or
|
||||
readline function at the top of the stack of unused
|
||||
keypresses
|
||||
*/
|
||||
input_unreadch(c);
|
||||
}
|
||||
else
|
||||
{
|
||||
append_format(stderr_buffer,
|
||||
_(L"%ls: Unknown input function '%ls'\n"),
|
||||
argv[0],
|
||||
argv[i]);
|
||||
builtin_print_help(parser, argv[0], stderr_buffer);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Check for invalid switch combinations
|
||||
*/
|
||||
if ((search_mode || line_mode || cursor_mode) && (argc-woptind > 1))
|
||||
{
|
||||
|
||||
append_format(stderr_buffer,
|
||||
argv[0],
|
||||
L": Too many arguments\n",
|
||||
NULL);
|
||||
builtin_print_help(parser, argv[0], stderr_buffer);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ((buffer_part || tokenize || cut_at_cursor) && (cursor_mode || line_mode || search_mode))
|
||||
{
|
||||
append_format(stderr_buffer,
|
||||
BUILTIN_ERR_COMBO,
|
||||
argv[0]);
|
||||
|
||||
builtin_print_help(parser, argv[0], stderr_buffer);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
if ((tokenize || cut_at_cursor) && (argc-woptind))
|
||||
{
|
||||
append_format(stderr_buffer,
|
||||
BUILTIN_ERR_COMBO2,
|
||||
argv[0],
|
||||
L"--cut-at-cursor and --tokenize can not be used when setting the commandline");
|
||||
|
||||
|
||||
builtin_print_help(parser, argv[0], stderr_buffer);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (append_mode && !(argc-woptind))
|
||||
{
|
||||
append_format(stderr_buffer,
|
||||
BUILTIN_ERR_COMBO2,
|
||||
argv[0],
|
||||
L"insertion mode switches can not be used when not in insertion mode");
|
||||
|
||||
builtin_print_help(parser, argv[0], stderr_buffer);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
Set default modes
|
||||
*/
|
||||
if (!append_mode)
|
||||
{
|
||||
append_mode = REPLACE_MODE;
|
||||
}
|
||||
|
||||
if (!buffer_part)
|
||||
{
|
||||
buffer_part = STRING_MODE;
|
||||
}
|
||||
|
||||
if (cursor_mode)
|
||||
{
|
||||
if (argc-woptind)
|
||||
{
|
||||
wchar_t *endptr;
|
||||
long new_pos;
|
||||
errno = 0;
|
||||
|
||||
new_pos = wcstol(argv[woptind], &endptr, 10);
|
||||
if (*endptr || errno)
|
||||
{
|
||||
append_format(stderr_buffer,
|
||||
BUILTIN_ERR_NOT_NUMBER,
|
||||
argv[0],
|
||||
argv[woptind]);
|
||||
builtin_print_help(parser, argv[0], stderr_buffer);
|
||||
}
|
||||
|
||||
current_buffer = reader_get_buffer();
|
||||
new_pos = maxi(0L, mini(new_pos, (long)wcslen(current_buffer)));
|
||||
reader_set_buffer(current_buffer, (size_t)new_pos);
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
append_format(stdout_buffer, L"%lu\n", (unsigned long)reader_get_cursor_pos());
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (line_mode)
|
||||
{
|
||||
size_t pos = reader_get_cursor_pos();
|
||||
const wchar_t *buff = reader_get_buffer();
|
||||
append_format(stdout_buffer, L"%lu\n", (unsigned long)parse_util_lineno(buff, pos));
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
if (search_mode)
|
||||
{
|
||||
return !reader_search_mode();
|
||||
}
|
||||
|
||||
|
||||
switch (buffer_part)
|
||||
{
|
||||
case STRING_MODE:
|
||||
{
|
||||
begin = get_buffer();
|
||||
end = begin+wcslen(begin);
|
||||
break;
|
||||
}
|
||||
|
||||
case PROCESS_MODE:
|
||||
{
|
||||
parse_util_process_extent(get_buffer(),
|
||||
get_cursor_pos(),
|
||||
&begin,
|
||||
&end);
|
||||
break;
|
||||
}
|
||||
|
||||
case JOB_MODE:
|
||||
{
|
||||
parse_util_job_extent(get_buffer(),
|
||||
get_cursor_pos(),
|
||||
&begin,
|
||||
&end);
|
||||
break;
|
||||
}
|
||||
|
||||
case TOKEN_MODE:
|
||||
{
|
||||
parse_util_token_extent(get_buffer(),
|
||||
get_cursor_pos(),
|
||||
&begin,
|
||||
&end,
|
||||
0, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
switch (argc-woptind)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
write_part(begin, end, cut_at_cursor, tokenize);
|
||||
break;
|
||||
}
|
||||
|
||||
case 1:
|
||||
{
|
||||
replace_part(begin, end, argv[woptind], append_mode);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
wcstring sb = argv[woptind];
|
||||
int i;
|
||||
|
||||
for (i=woptind+1; i<argc; i++)
|
||||
{
|
||||
sb.push_back(L'\n');
|
||||
sb.append(argv[i]);
|
||||
}
|
||||
|
||||
replace_part(begin, end, sb.c_str(), append_mode);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
626
builtin_complete.cpp
Normal file
626
builtin_complete.cpp
Normal file
@@ -0,0 +1,626 @@
|
||||
/** \file builtin_complete.c Functions defining the complete builtin
|
||||
|
||||
Functions used for implementing the complete builtin.
|
||||
|
||||
*/
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <wchar.h>
|
||||
#include <wctype.h>
|
||||
#include <sys/types.h>
|
||||
#include <termios.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include "fallback.h"
|
||||
#include "util.h"
|
||||
|
||||
#include "wutil.h"
|
||||
#include "builtin.h"
|
||||
#include "common.h"
|
||||
#include "complete.h"
|
||||
#include "wgetopt.h"
|
||||
#include "parser.h"
|
||||
#include "reader.h"
|
||||
|
||||
|
||||
/**
|
||||
Internal storage for the builtin_complete_get_temporary_buffer() function.
|
||||
*/
|
||||
static const wchar_t *temporary_buffer;
|
||||
|
||||
/*
|
||||
builtin_complete_* are a set of rather silly looping functions that
|
||||
make sure that all the proper combinations of complete_add or
|
||||
complete_remove get called. This is needed since complete allows you
|
||||
to specify multiple switches on a single commandline, like 'complete
|
||||
-s a -s b -s c', but the complete_add function only accepts one
|
||||
short switch and one long switch.
|
||||
*/
|
||||
|
||||
/**
|
||||
Silly function
|
||||
*/
|
||||
static void builtin_complete_add2(const wchar_t *cmd,
|
||||
int cmd_type,
|
||||
const wchar_t *short_opt,
|
||||
const wcstring_list_t &gnu_opt,
|
||||
const wcstring_list_t &old_opt,
|
||||
int result_mode,
|
||||
const wchar_t *condition,
|
||||
const wchar_t *comp,
|
||||
const wchar_t *desc,
|
||||
int flags)
|
||||
{
|
||||
size_t i;
|
||||
const wchar_t *s;
|
||||
|
||||
for (s=short_opt; *s; s++)
|
||||
{
|
||||
complete_add(cmd,
|
||||
cmd_type,
|
||||
*s,
|
||||
0,
|
||||
0,
|
||||
result_mode,
|
||||
condition,
|
||||
comp,
|
||||
desc,
|
||||
flags);
|
||||
}
|
||||
|
||||
for (i=0; i<gnu_opt.size(); i++)
|
||||
{
|
||||
complete_add(cmd,
|
||||
cmd_type,
|
||||
0,
|
||||
gnu_opt.at(i).c_str(),
|
||||
0,
|
||||
result_mode,
|
||||
condition,
|
||||
comp,
|
||||
desc,
|
||||
flags);
|
||||
}
|
||||
|
||||
for (i=0; i<old_opt.size(); i++)
|
||||
{
|
||||
complete_add(cmd,
|
||||
cmd_type,
|
||||
0,
|
||||
old_opt.at(i).c_str(),
|
||||
1,
|
||||
result_mode,
|
||||
condition,
|
||||
comp,
|
||||
desc,
|
||||
flags);
|
||||
}
|
||||
|
||||
if (old_opt.empty() && gnu_opt.empty() && wcslen(short_opt) == 0)
|
||||
{
|
||||
complete_add(cmd,
|
||||
cmd_type,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
result_mode,
|
||||
condition,
|
||||
comp,
|
||||
desc,
|
||||
flags);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Silly function
|
||||
*/
|
||||
static void builtin_complete_add(const wcstring_list_t &cmd,
|
||||
const wcstring_list_t &path,
|
||||
const wchar_t *short_opt,
|
||||
wcstring_list_t &gnu_opt,
|
||||
wcstring_list_t &old_opt,
|
||||
int result_mode,
|
||||
int authoritative,
|
||||
const wchar_t *condition,
|
||||
const wchar_t *comp,
|
||||
const wchar_t *desc,
|
||||
int flags)
|
||||
{
|
||||
for (size_t i=0; i<cmd.size(); i++)
|
||||
{
|
||||
builtin_complete_add2(cmd.at(i).c_str(),
|
||||
COMMAND,
|
||||
short_opt,
|
||||
gnu_opt,
|
||||
old_opt,
|
||||
result_mode,
|
||||
condition,
|
||||
comp,
|
||||
desc,
|
||||
flags);
|
||||
|
||||
if (authoritative != -1)
|
||||
{
|
||||
complete_set_authoritative(cmd.at(i).c_str(),
|
||||
COMMAND,
|
||||
authoritative);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for (size_t i=0; i<path.size(); i++)
|
||||
{
|
||||
builtin_complete_add2(path.at(i).c_str(),
|
||||
PATH,
|
||||
short_opt,
|
||||
gnu_opt,
|
||||
old_opt,
|
||||
result_mode,
|
||||
condition,
|
||||
comp,
|
||||
desc,
|
||||
flags);
|
||||
|
||||
if (authoritative != -1)
|
||||
{
|
||||
complete_set_authoritative(path.at(i).c_str(),
|
||||
PATH,
|
||||
authoritative);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Silly function
|
||||
*/
|
||||
static void builtin_complete_remove3(const wchar_t *cmd,
|
||||
int cmd_type,
|
||||
wchar_t short_opt,
|
||||
const wcstring_list_t &long_opt)
|
||||
{
|
||||
for (size_t i=0; i<long_opt.size(); i++)
|
||||
{
|
||||
complete_remove(cmd,
|
||||
cmd_type,
|
||||
short_opt,
|
||||
long_opt.at(i).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Silly function
|
||||
*/
|
||||
static void builtin_complete_remove2(const wchar_t *cmd,
|
||||
int cmd_type,
|
||||
const wchar_t *short_opt,
|
||||
const wcstring_list_t &gnu_opt,
|
||||
const wcstring_list_t &old_opt)
|
||||
{
|
||||
const wchar_t *s = (wchar_t *)short_opt;
|
||||
if (*s)
|
||||
{
|
||||
for (; *s; s++)
|
||||
{
|
||||
if (old_opt.empty() && gnu_opt.empty())
|
||||
{
|
||||
complete_remove(cmd,
|
||||
cmd_type,
|
||||
*s,
|
||||
0);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
builtin_complete_remove3(cmd,
|
||||
cmd_type,
|
||||
*s,
|
||||
gnu_opt);
|
||||
builtin_complete_remove3(cmd,
|
||||
cmd_type,
|
||||
*s,
|
||||
old_opt);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
builtin_complete_remove3(cmd,
|
||||
cmd_type,
|
||||
0,
|
||||
gnu_opt);
|
||||
builtin_complete_remove3(cmd,
|
||||
cmd_type,
|
||||
0,
|
||||
old_opt);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
Silly function
|
||||
*/
|
||||
static void builtin_complete_remove(const wcstring_list_t &cmd,
|
||||
const wcstring_list_t &path,
|
||||
const wchar_t *short_opt,
|
||||
const wcstring_list_t &gnu_opt,
|
||||
const wcstring_list_t &old_opt)
|
||||
{
|
||||
for (size_t i=0; i<cmd.size(); i++)
|
||||
{
|
||||
builtin_complete_remove2(cmd.at(i).c_str(),
|
||||
COMMAND,
|
||||
short_opt,
|
||||
gnu_opt,
|
||||
old_opt);
|
||||
}
|
||||
|
||||
for (size_t i=0; i<path.size(); i++)
|
||||
{
|
||||
builtin_complete_remove2(path.at(i).c_str(),
|
||||
PATH,
|
||||
short_opt,
|
||||
gnu_opt,
|
||||
old_opt);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
const wchar_t *builtin_complete_get_temporary_buffer()
|
||||
{
|
||||
ASSERT_IS_MAIN_THREAD();
|
||||
return temporary_buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
The complete builtin. Used for specifying programmable
|
||||
tab-completions. Calls the functions in complete.c for any heavy
|
||||
lifting. Defined in builtin_complete.c
|
||||
*/
|
||||
static int builtin_complete(parser_t &parser, wchar_t **argv)
|
||||
{
|
||||
ASSERT_IS_MAIN_THREAD();
|
||||
bool res=false;
|
||||
int argc=0;
|
||||
int result_mode=SHARED;
|
||||
int remove = 0;
|
||||
int authoritative = -1;
|
||||
int flags = COMPLETE_AUTO_SPACE;
|
||||
|
||||
wcstring short_opt;
|
||||
wcstring_list_t gnu_opt, old_opt;
|
||||
const wchar_t *comp=L"", *desc=L"", *condition=L"";
|
||||
|
||||
bool do_complete = false;
|
||||
wcstring do_complete_param;
|
||||
|
||||
wcstring_list_t cmd;
|
||||
wcstring_list_t path;
|
||||
|
||||
static int recursion_level=0;
|
||||
|
||||
argc = builtin_count_args(argv);
|
||||
|
||||
woptind=0;
|
||||
|
||||
while (! res)
|
||||
{
|
||||
static const struct woption
|
||||
long_options[] =
|
||||
{
|
||||
{
|
||||
L"exclusive", no_argument, 0, 'x'
|
||||
}
|
||||
,
|
||||
{
|
||||
L"no-files", no_argument, 0, 'f'
|
||||
}
|
||||
,
|
||||
{
|
||||
L"require-parameter", no_argument, 0, 'r'
|
||||
}
|
||||
,
|
||||
{
|
||||
L"path", required_argument, 0, 'p'
|
||||
}
|
||||
,
|
||||
{
|
||||
L"command", required_argument, 0, 'c'
|
||||
}
|
||||
,
|
||||
{
|
||||
L"short-option", required_argument, 0, 's'
|
||||
}
|
||||
,
|
||||
{
|
||||
L"long-option", required_argument, 0, 'l'
|
||||
}
|
||||
,
|
||||
{
|
||||
L"old-option", required_argument, 0, 'o'
|
||||
}
|
||||
,
|
||||
{
|
||||
L"description", required_argument, 0, 'd'
|
||||
}
|
||||
,
|
||||
{
|
||||
L"arguments", required_argument, 0, 'a'
|
||||
}
|
||||
,
|
||||
{
|
||||
L"erase", no_argument, 0, 'e'
|
||||
}
|
||||
,
|
||||
{
|
||||
L"unauthoritative", no_argument, 0, 'u'
|
||||
}
|
||||
,
|
||||
{
|
||||
L"authoritative", no_argument, 0, 'A'
|
||||
}
|
||||
,
|
||||
{
|
||||
L"condition", required_argument, 0, 'n'
|
||||
}
|
||||
,
|
||||
{
|
||||
L"do-complete", optional_argument, 0, 'C'
|
||||
}
|
||||
,
|
||||
{
|
||||
L"help", no_argument, 0, 'h'
|
||||
}
|
||||
,
|
||||
{
|
||||
0, 0, 0, 0
|
||||
}
|
||||
}
|
||||
;
|
||||
|
||||
int opt_index = 0;
|
||||
|
||||
int opt = wgetopt_long(argc,
|
||||
argv,
|
||||
L"a:c:p:s:l:o:d:frxeuAn:C::h",
|
||||
long_options,
|
||||
&opt_index);
|
||||
if (opt == -1)
|
||||
break;
|
||||
|
||||
switch (opt)
|
||||
{
|
||||
case 0:
|
||||
if (long_options[opt_index].flag != 0)
|
||||
break;
|
||||
append_format(stderr_buffer,
|
||||
BUILTIN_ERR_UNKNOWN,
|
||||
argv[0],
|
||||
long_options[opt_index].name);
|
||||
builtin_print_help(parser, argv[0], stderr_buffer);
|
||||
|
||||
|
||||
res = true;
|
||||
break;
|
||||
|
||||
case 'x':
|
||||
result_mode |= EXCLUSIVE;
|
||||
break;
|
||||
|
||||
case 'f':
|
||||
result_mode |= NO_FILES;
|
||||
break;
|
||||
|
||||
case 'r':
|
||||
result_mode |= NO_COMMON;
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
case 'c':
|
||||
{
|
||||
wcstring tmp = woptarg;
|
||||
if (unescape_string(tmp, 1))
|
||||
{
|
||||
if (opt=='p')
|
||||
path.push_back(tmp);
|
||||
else
|
||||
cmd.push_back(tmp);
|
||||
}
|
||||
else
|
||||
{
|
||||
append_format(stderr_buffer, L"%ls: Invalid token '%ls'\n", argv[0], woptarg);
|
||||
res = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'd':
|
||||
desc = woptarg;
|
||||
break;
|
||||
|
||||
case 'u':
|
||||
authoritative=0;
|
||||
break;
|
||||
|
||||
case 'A':
|
||||
authoritative=1;
|
||||
break;
|
||||
|
||||
case 's':
|
||||
short_opt.append(woptarg);
|
||||
break;
|
||||
|
||||
case 'l':
|
||||
gnu_opt.push_back(woptarg);
|
||||
break;
|
||||
|
||||
case 'o':
|
||||
old_opt.push_back(woptarg);
|
||||
break;
|
||||
|
||||
case 'a':
|
||||
comp = woptarg;
|
||||
break;
|
||||
|
||||
case 'e':
|
||||
remove = 1;
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
condition = woptarg;
|
||||
break;
|
||||
|
||||
case 'C':
|
||||
do_complete = true;
|
||||
do_complete_param = woptarg ? woptarg : reader_get_buffer();
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
builtin_print_help(parser, argv[0], stdout_buffer);
|
||||
return 0;
|
||||
|
||||
case '?':
|
||||
builtin_unknown_option(parser, argv[0], argv[woptind-1]);
|
||||
res = true;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!res)
|
||||
{
|
||||
if (condition && wcslen(condition))
|
||||
{
|
||||
if (parser.test(condition))
|
||||
{
|
||||
append_format(stderr_buffer,
|
||||
L"%ls: Condition '%ls' contained a syntax error\n",
|
||||
argv[0],
|
||||
condition);
|
||||
|
||||
parser.test(condition, NULL, &stderr_buffer, argv[0]);
|
||||
|
||||
res = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!res)
|
||||
{
|
||||
if (comp && wcslen(comp))
|
||||
{
|
||||
if (parser.test_args(comp, 0, 0))
|
||||
{
|
||||
append_format(stderr_buffer,
|
||||
L"%ls: Completion '%ls' contained a syntax error\n",
|
||||
argv[0],
|
||||
comp);
|
||||
|
||||
parser.test_args(comp, &stderr_buffer, argv[0]);
|
||||
|
||||
res = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!res)
|
||||
{
|
||||
if (do_complete)
|
||||
{
|
||||
const wchar_t *token;
|
||||
|
||||
parse_util_token_extent(do_complete_param.c_str(), do_complete_param.size(), &token, 0, 0, 0);
|
||||
|
||||
const wchar_t *prev_temporary_buffer = temporary_buffer;
|
||||
temporary_buffer = do_complete_param.c_str();
|
||||
|
||||
if (recursion_level < 1)
|
||||
{
|
||||
recursion_level++;
|
||||
|
||||
std::vector<completion_t> comp;
|
||||
complete(do_complete_param, comp, COMPLETION_REQUEST_DEFAULT);
|
||||
|
||||
for (size_t i=0; i< comp.size() ; i++)
|
||||
{
|
||||
const completion_t &next = comp.at(i);
|
||||
|
||||
const wchar_t *prepend;
|
||||
|
||||
if (next.flags & COMPLETE_REPLACES_TOKEN)
|
||||
{
|
||||
prepend = L"";
|
||||
}
|
||||
else
|
||||
{
|
||||
prepend = token;
|
||||
}
|
||||
|
||||
|
||||
if (!(next.description).empty())
|
||||
{
|
||||
append_format(stdout_buffer, L"%ls%ls\t%ls\n", prepend, next.completion.c_str(), next.description.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
append_format(stdout_buffer, L"%ls%ls\n", prepend, next.completion.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
recursion_level--;
|
||||
}
|
||||
|
||||
temporary_buffer = prev_temporary_buffer;
|
||||
|
||||
}
|
||||
else if (woptind != argc)
|
||||
{
|
||||
append_format(stderr_buffer,
|
||||
_(L"%ls: Too many arguments\n"),
|
||||
argv[0]);
|
||||
builtin_print_help(parser, argv[0], stderr_buffer);
|
||||
|
||||
res = true;
|
||||
}
|
||||
else if (cmd.empty() && path.empty())
|
||||
{
|
||||
/* No arguments specified, meaning we print the definitions of
|
||||
* all specified completions to stdout.*/
|
||||
complete_print(stdout_buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (remove)
|
||||
{
|
||||
builtin_complete_remove(cmd,
|
||||
path,
|
||||
short_opt.c_str(),
|
||||
gnu_opt,
|
||||
old_opt);
|
||||
}
|
||||
else
|
||||
{
|
||||
builtin_complete_add(cmd,
|
||||
path,
|
||||
short_opt.c_str(),
|
||||
gnu_opt,
|
||||
old_opt,
|
||||
result_mode,
|
||||
authoritative,
|
||||
condition,
|
||||
comp,
|
||||
desc,
|
||||
flags);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return res ? 1 : 0;
|
||||
}
|
||||
351
builtin_jobs.cpp
Normal file
351
builtin_jobs.cpp
Normal file
@@ -0,0 +1,351 @@
|
||||
/** \file builtin_jobs.c
|
||||
Functions for executing the jobs builtin.
|
||||
*/
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <wchar.h>
|
||||
#include <unistd.h>
|
||||
#include <termios.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <string.h>
|
||||
#include <wctype.h>
|
||||
|
||||
#include "fallback.h"
|
||||
#include "util.h"
|
||||
|
||||
#include "wutil.h"
|
||||
#include "builtin.h"
|
||||
#include "proc.h"
|
||||
#include "parser.h"
|
||||
#include "common.h"
|
||||
#include "wgetopt.h"
|
||||
|
||||
|
||||
/**
|
||||
Print modes for the jobs builtin
|
||||
*/
|
||||
enum
|
||||
{
|
||||
JOBS_DEFAULT, /**< Print lots of general info */
|
||||
JOBS_PRINT_PID, /**< Print pid of each process in job */
|
||||
JOBS_PRINT_COMMAND, /**< Print command name of each process in job */
|
||||
JOBS_PRINT_GROUP, /**< Print group id of job */
|
||||
}
|
||||
;
|
||||
|
||||
|
||||
|
||||
#ifdef HAVE__PROC_SELF_STAT
|
||||
/**
|
||||
Calculates the cpu usage (in percent) of the specified job.
|
||||
*/
|
||||
static int cpu_use(const job_t *j)
|
||||
{
|
||||
double u=0;
|
||||
process_t *p;
|
||||
|
||||
for (p=j->first_process; p; p=p->next)
|
||||
{
|
||||
struct timeval t;
|
||||
int jiffies;
|
||||
gettimeofday(&t, 0);
|
||||
jiffies = proc_get_jiffies(p);
|
||||
|
||||
double t1 = 1000000.0*p->last_time.tv_sec+p->last_time.tv_usec;
|
||||
double t2 = 1000000.0*t.tv_sec+t.tv_usec;
|
||||
|
||||
/* fwprintf( stderr, L"t1 %f t2 %f p1 %d p2 %d\n",
|
||||
t1, t2, jiffies, p->last_jiffies );
|
||||
*/
|
||||
|
||||
u += ((double)(jiffies-p->last_jiffies))/(t2-t1);
|
||||
}
|
||||
return u*1000000;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
Print information about the specified job
|
||||
*/
|
||||
static void builtin_jobs_print(const job_t *j, int mode, int header)
|
||||
{
|
||||
process_t *p;
|
||||
switch (mode)
|
||||
{
|
||||
case JOBS_DEFAULT:
|
||||
{
|
||||
|
||||
if (header)
|
||||
{
|
||||
/*
|
||||
Print table header before first job
|
||||
*/
|
||||
stdout_buffer.append(_(L"Job\tGroup\t"));
|
||||
#ifdef HAVE__PROC_SELF_STAT
|
||||
stdout_buffer.append(_(L"CPU\t"));
|
||||
#endif
|
||||
stdout_buffer.append(_(L"State\tCommand\n"));
|
||||
}
|
||||
|
||||
append_format(stdout_buffer, L"%d\t%d\t", j->job_id, j->pgid);
|
||||
|
||||
#ifdef HAVE__PROC_SELF_STAT
|
||||
append_format(stdout_buffer, L"%d%%\t", cpu_use(j));
|
||||
#endif
|
||||
stdout_buffer.append(job_is_stopped(j)?_(L"stopped"):_(L"running"));
|
||||
stdout_buffer.append(L"\t");
|
||||
stdout_buffer.append(j->command_wcstr());
|
||||
stdout_buffer.append(L"\n");
|
||||
break;
|
||||
}
|
||||
|
||||
case JOBS_PRINT_GROUP:
|
||||
{
|
||||
if (header)
|
||||
{
|
||||
/*
|
||||
Print table header before first job
|
||||
*/
|
||||
stdout_buffer.append(_(L"Group\n"));
|
||||
}
|
||||
append_format(stdout_buffer, L"%d\n", j->pgid);
|
||||
break;
|
||||
}
|
||||
|
||||
case JOBS_PRINT_PID:
|
||||
{
|
||||
if (header)
|
||||
{
|
||||
/*
|
||||
Print table header before first job
|
||||
*/
|
||||
stdout_buffer.append(_(L"Procces\n"));
|
||||
}
|
||||
|
||||
for (p=j->first_process; p; p=p->next)
|
||||
{
|
||||
append_format(stdout_buffer, L"%d\n", p->pid);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case JOBS_PRINT_COMMAND:
|
||||
{
|
||||
if (header)
|
||||
{
|
||||
/*
|
||||
Print table header before first job
|
||||
*/
|
||||
stdout_buffer.append(_(L"Command\n"));
|
||||
}
|
||||
|
||||
for (p=j->first_process; p; p=p->next)
|
||||
{
|
||||
append_format(stdout_buffer, L"%ls\n", p->argv0());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
The jobs builtin. Used fopr printing running jobs. Defined in builtin_jobs.c.
|
||||
*/
|
||||
static int builtin_jobs(parser_t &parser, wchar_t **argv)
|
||||
{
|
||||
int argc=0;
|
||||
int found=0;
|
||||
int mode=JOBS_DEFAULT;
|
||||
int print_last = 0;
|
||||
const job_t *j;
|
||||
|
||||
argc = builtin_count_args(argv);
|
||||
woptind=0;
|
||||
|
||||
while (1)
|
||||
{
|
||||
static const struct woption
|
||||
long_options[] =
|
||||
{
|
||||
{
|
||||
L"pid", no_argument, 0, 'p'
|
||||
}
|
||||
,
|
||||
{
|
||||
L"command", no_argument, 0, 'c'
|
||||
}
|
||||
,
|
||||
{
|
||||
L"group", no_argument, 0, 'g'
|
||||
}
|
||||
,
|
||||
{
|
||||
L"last", no_argument, 0, 'l'
|
||||
}
|
||||
,
|
||||
{
|
||||
L"help", no_argument, 0, 'h'
|
||||
}
|
||||
,
|
||||
{
|
||||
0, 0, 0, 0
|
||||
}
|
||||
}
|
||||
;
|
||||
|
||||
int opt_index = 0;
|
||||
|
||||
int opt = wgetopt_long(argc,
|
||||
argv,
|
||||
L"pclgh",
|
||||
long_options,
|
||||
&opt_index);
|
||||
if (opt == -1)
|
||||
break;
|
||||
|
||||
switch (opt)
|
||||
{
|
||||
case 0:
|
||||
if (long_options[opt_index].flag != 0)
|
||||
break;
|
||||
append_format(stderr_buffer,
|
||||
BUILTIN_ERR_UNKNOWN,
|
||||
argv[0],
|
||||
long_options[opt_index].name);
|
||||
|
||||
builtin_print_help(parser, argv[0], stderr_buffer);
|
||||
|
||||
|
||||
return 1;
|
||||
|
||||
|
||||
case 'p':
|
||||
mode=JOBS_PRINT_PID;
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
mode=JOBS_PRINT_COMMAND;
|
||||
break;
|
||||
|
||||
case 'g':
|
||||
mode=JOBS_PRINT_GROUP;
|
||||
break;
|
||||
|
||||
case 'l':
|
||||
{
|
||||
print_last = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
case 'h':
|
||||
builtin_print_help(parser, argv[0], stdout_buffer);
|
||||
return 0;
|
||||
|
||||
case '?':
|
||||
builtin_unknown_option(parser, argv[0], argv[woptind-1]);
|
||||
return 1;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Do not babble if not interactive
|
||||
*/
|
||||
if (builtin_out_redirect)
|
||||
{
|
||||
found=1;
|
||||
}
|
||||
|
||||
if (print_last)
|
||||
{
|
||||
/*
|
||||
Ignore unconstructed jobs, i.e. ourself.
|
||||
*/
|
||||
job_iterator_t jobs;
|
||||
const job_t *j;
|
||||
while ((j = jobs.next()))
|
||||
{
|
||||
|
||||
if ((j->flags & JOB_CONSTRUCTED) && !job_is_completed(j))
|
||||
{
|
||||
builtin_jobs_print(j, mode, !found);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
if (woptind < argc)
|
||||
{
|
||||
int i;
|
||||
|
||||
found = 1;
|
||||
|
||||
for (i=woptind; i<argc; i++)
|
||||
{
|
||||
int pid;
|
||||
wchar_t *end;
|
||||
errno=0;
|
||||
pid=fish_wcstoi(argv[i], &end, 10);
|
||||
if (errno || *end)
|
||||
{
|
||||
append_format(stderr_buffer,
|
||||
_(L"%ls: '%ls' is not a job\n"),
|
||||
argv[0],
|
||||
argv[i]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
j = job_get_from_pid(pid);
|
||||
|
||||
if (j && !job_is_completed(j))
|
||||
{
|
||||
builtin_jobs_print(j, mode, !found);
|
||||
}
|
||||
else
|
||||
{
|
||||
append_format(stderr_buffer,
|
||||
_(L"%ls: No suitable job: %d\n"),
|
||||
argv[0],
|
||||
pid);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
job_iterator_t jobs;
|
||||
const job_t *j;
|
||||
while ((j = jobs.next()))
|
||||
{
|
||||
/*
|
||||
Ignore unconstructed jobs, i.e. ourself.
|
||||
*/
|
||||
if ((j->flags & JOB_CONSTRUCTED) && !job_is_completed(j))
|
||||
{
|
||||
builtin_jobs_print(j, mode, !found);
|
||||
found = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
append_format(stdout_buffer,
|
||||
_(L"%ls: There are no jobs\n"),
|
||||
argv[0]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
777
builtin_printf.cpp
Normal file
777
builtin_printf.cpp
Normal file
@@ -0,0 +1,777 @@
|
||||
/* printf - format and print data
|
||||
Copyright (C) 1990-2007 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software Foundation,
|
||||
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
|
||||
|
||||
/* Usage: printf format [argument...]
|
||||
|
||||
A front end to the printf function that lets it be used from the shell.
|
||||
|
||||
Backslash escapes:
|
||||
|
||||
\" = double quote
|
||||
\\ = backslash
|
||||
\a = alert (bell)
|
||||
\b = backspace
|
||||
\c = produce no further output
|
||||
\f = form feed
|
||||
\n = new line
|
||||
\r = carriage return
|
||||
\t = horizontal tab
|
||||
\v = vertical tab
|
||||
\ooo = octal number (ooo is 1 to 3 digits)
|
||||
\xhh = hexadecimal number (hhh is 1 to 2 digits)
|
||||
\uhhhh = 16-bit Unicode character (hhhh is 4 digits)
|
||||
\Uhhhhhhhh = 32-bit Unicode character (hhhhhhhh is 8 digits)
|
||||
|
||||
Additional directive:
|
||||
|
||||
%b = print an argument string, interpreting backslash escapes,
|
||||
except that octal escapes are of the form \0 or \0ooo.
|
||||
|
||||
The `format' argument is re-used as many times as necessary
|
||||
to convert all of the given arguments.
|
||||
|
||||
David MacKenzie <djm@gnu.ai.mit.edu> */
|
||||
|
||||
/* This file has been imported from source code of printf command in GNU Coreutils version 6.9 */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
struct builtin_printf_state_t
|
||||
{
|
||||
/* The status of the operation */
|
||||
int exit_code;
|
||||
|
||||
/* Whether we should stop outputting. This gets set in the case of an error, and also with the \c escape. */
|
||||
bool early_exit;
|
||||
|
||||
builtin_printf_state_t() : exit_code(0), early_exit(false)
|
||||
{
|
||||
}
|
||||
|
||||
void verify_numeric(const wchar_t *s, const wchar_t *end, int errcode);
|
||||
|
||||
void print_direc(const wchar_t *start, size_t length, wchar_t conversion,
|
||||
bool have_field_width, int field_width,
|
||||
bool have_precision, int precision,
|
||||
wchar_t const *argument);
|
||||
|
||||
int print_formatted(const wchar_t *format, int argc, wchar_t **argv);
|
||||
|
||||
void fatal_error(const wchar_t *format, ...);
|
||||
|
||||
long print_esc(const wchar_t *escstart, bool octal_0);
|
||||
void print_esc_string(const wchar_t *str);
|
||||
void print_esc_char(wchar_t c);
|
||||
|
||||
void append_output(wchar_t c);
|
||||
void append_output(const wchar_t *c);
|
||||
void append_format_output(const wchar_t *fmt, ...);
|
||||
};
|
||||
|
||||
static bool is_octal_digit(wchar_t c)
|
||||
{
|
||||
return c != L'\0' && wcschr(L"01234567", c) != NULL;
|
||||
}
|
||||
|
||||
static bool is_hex_digit(wchar_t c)
|
||||
{
|
||||
return c != L'\0' && wcschr(L"0123456789ABCDEFabcdef", c) != NULL;
|
||||
}
|
||||
|
||||
static int hex_to_bin(const wchar_t &c)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case L'0':
|
||||
return 0;
|
||||
case L'1':
|
||||
return 1;
|
||||
case L'2':
|
||||
return 2;
|
||||
case L'3':
|
||||
return 3;
|
||||
case L'4':
|
||||
return 4;
|
||||
case L'5':
|
||||
return 5;
|
||||
case L'6':
|
||||
return 6;
|
||||
case L'7':
|
||||
return 7;
|
||||
case L'8':
|
||||
return 8;
|
||||
case L'9':
|
||||
return 9;
|
||||
case L'a':
|
||||
case L'A':
|
||||
return 10;
|
||||
case L'b':
|
||||
case L'B':
|
||||
return 11;
|
||||
case L'c':
|
||||
case L'C':
|
||||
return 12;
|
||||
case L'd':
|
||||
case L'D':
|
||||
return 13;
|
||||
case L'e':
|
||||
case L'E':
|
||||
return 14;
|
||||
case L'f':
|
||||
case L'F':
|
||||
return 15;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static int octal_to_bin(wchar_t c)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case L'0':
|
||||
return 0;
|
||||
case L'1':
|
||||
return 1;
|
||||
case L'2':
|
||||
return 2;
|
||||
case L'3':
|
||||
return 3;
|
||||
case L'4':
|
||||
return 4;
|
||||
case L'5':
|
||||
return 5;
|
||||
case L'6':
|
||||
return 6;
|
||||
case L'7':
|
||||
return 7;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* This message appears in N_() here rather than just in _() below because
|
||||
the sole use would have been in a #define. */
|
||||
static wchar_t const *const cfcc_msg =
|
||||
N_(L"warning: %ls: character(s) following character constant have been ignored");
|
||||
|
||||
double C_STRTOD(wchar_t const *nptr, wchar_t **endptr)
|
||||
{
|
||||
double r;
|
||||
|
||||
const wcstring saved_locale = wsetlocale(LC_NUMERIC, NULL);
|
||||
|
||||
if (!saved_locale.empty())
|
||||
{
|
||||
wsetlocale(LC_NUMERIC, L"C");
|
||||
}
|
||||
|
||||
r = wcstod(nptr, endptr);
|
||||
|
||||
if (!saved_locale.empty())
|
||||
{
|
||||
wsetlocale(LC_NUMERIC, saved_locale.c_str());
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static inline unsigned wchar_t to_uwchar_t(wchar_t ch)
|
||||
{
|
||||
return ch;
|
||||
}
|
||||
|
||||
void builtin_printf_state_t::fatal_error(const wchar_t *fmt, ...)
|
||||
{
|
||||
// Don't error twice
|
||||
if (early_exit)
|
||||
return;
|
||||
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
wcstring errstr = vformat_string(fmt, va);
|
||||
va_end(va);
|
||||
stderr_buffer.append(errstr);
|
||||
if (! string_suffixes_string(L"\n", errstr))
|
||||
stderr_buffer.push_back(L'\n');
|
||||
|
||||
this->exit_code = STATUS_BUILTIN_ERROR;
|
||||
this->early_exit = true;
|
||||
}
|
||||
|
||||
void builtin_printf_state_t::append_output(wchar_t c)
|
||||
{
|
||||
// Don't output if we're done
|
||||
if (early_exit)
|
||||
return;
|
||||
|
||||
stdout_buffer.push_back(c);
|
||||
}
|
||||
|
||||
void builtin_printf_state_t::append_output(const wchar_t *c)
|
||||
{
|
||||
// Don't output if we're done
|
||||
if (early_exit)
|
||||
return;
|
||||
|
||||
stdout_buffer.append(c);
|
||||
}
|
||||
|
||||
void builtin_printf_state_t::append_format_output(const wchar_t *fmt, ...)
|
||||
{
|
||||
// Don't output if we're done
|
||||
if (early_exit)
|
||||
return;
|
||||
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
append_formatv(stdout_buffer, fmt, va);
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
|
||||
void builtin_printf_state_t::verify_numeric(const wchar_t *s, const wchar_t *end, int errcode)
|
||||
{
|
||||
if (errcode != 0)
|
||||
{
|
||||
this->fatal_error(L"%ls: %s", s, strerror(errcode));
|
||||
}
|
||||
else if (*end)
|
||||
{
|
||||
if (s == end)
|
||||
this->fatal_error(_(L"%ls: expected a numeric value"), s);
|
||||
else
|
||||
this->fatal_error(_(L"%ls: value not completely converted"), s);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static T raw_string_to_scalar_type(const wchar_t *s, wchar_t ** end);
|
||||
|
||||
// we use wcstoll instead of wcstoimax because FreeBSD 8 has busted wcstoumax and wcstoimax - see #626
|
||||
template<>
|
||||
intmax_t raw_string_to_scalar_type(const wchar_t *s, wchar_t ** end)
|
||||
{
|
||||
return wcstoll(s, end, 0);
|
||||
}
|
||||
|
||||
template<>
|
||||
uintmax_t raw_string_to_scalar_type(const wchar_t *s, wchar_t ** end)
|
||||
{
|
||||
return wcstoull(s, end, 0);
|
||||
}
|
||||
|
||||
template<>
|
||||
long double raw_string_to_scalar_type(const wchar_t *s, wchar_t ** end)
|
||||
{
|
||||
return C_STRTOD(s, end);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static T string_to_scalar_type(const wchar_t *s, builtin_printf_state_t *state)
|
||||
{
|
||||
T val;
|
||||
if (*s == L'\"' || *s == L'\'')
|
||||
{
|
||||
unsigned wchar_t ch = *++s;
|
||||
val = ch;
|
||||
}
|
||||
else
|
||||
{
|
||||
wchar_t *end = NULL;
|
||||
errno = 0;
|
||||
val = raw_string_to_scalar_type<T>(s, &end);
|
||||
state->verify_numeric(s, end, errno);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
/* Output a single-character \ escape. */
|
||||
|
||||
void builtin_printf_state_t::print_esc_char(wchar_t c)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case L'a': /* Alert. */
|
||||
this->append_output(L'\a');
|
||||
break;
|
||||
case L'b': /* Backspace. */
|
||||
this->append_output(L'\b');
|
||||
break;
|
||||
case L'c': /* Cancel the rest of the output. */
|
||||
this->early_exit = true;
|
||||
break;
|
||||
case L'f': /* Form feed. */
|
||||
this->append_output(L'\f');
|
||||
break;
|
||||
case L'n': /* New line. */
|
||||
this->append_output(L'\n');
|
||||
break;
|
||||
case L'r': /* Carriage return. */
|
||||
this->append_output(L'\r');
|
||||
break;
|
||||
case L't': /* Horizontal tab. */
|
||||
this->append_output(L'\t');
|
||||
break;
|
||||
case L'v': /* Vertical tab. */
|
||||
this->append_output(L'\v');
|
||||
break;
|
||||
default:
|
||||
this->append_output(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Print a \ escape sequence starting at ESCSTART.
|
||||
Return the number of characters in the escape sequence
|
||||
besides the backslash.
|
||||
If OCTAL_0 is nonzero, octal escapes are of the form \0ooo, where o
|
||||
is an octal digit; otherwise they are of the form \ooo. */
|
||||
long builtin_printf_state_t::print_esc(const wchar_t *escstart, bool octal_0)
|
||||
{
|
||||
const wchar_t *p = escstart + 1;
|
||||
int esc_value = 0; /* Value of \nnn escape. */
|
||||
int esc_length; /* Length of \nnn escape. */
|
||||
|
||||
if (*p == L'x')
|
||||
{
|
||||
/* A hexadecimal \xhh escape sequence must have 1 or 2 hex. digits. */
|
||||
for (esc_length = 0, ++p; esc_length < 2 && is_hex_digit(*p); ++esc_length, ++p)
|
||||
esc_value = esc_value * 16 + hex_to_bin(*p);
|
||||
if (esc_length == 0)
|
||||
this->fatal_error(_(L"missing hexadecimal number in escape"));
|
||||
this->append_format_output(L"%lc", esc_value);
|
||||
}
|
||||
else if (is_octal_digit(*p))
|
||||
{
|
||||
/* Parse \0ooo (if octal_0 && *p == L'0') or \ooo (otherwise).
|
||||
Allow \ooo if octal_0 && *p != L'0'; this is an undocumented
|
||||
extension to POSIX that is compatible with Bash 2.05b. */
|
||||
for (esc_length = 0, p += octal_0 && *p == L'0'; esc_length < 3 && is_octal_digit(*p); ++esc_length, ++p)
|
||||
esc_value = esc_value * 8 + octal_to_bin(*p);
|
||||
this->append_format_output(L"%c", esc_value);
|
||||
}
|
||||
else if (*p && wcschr(L"\"\\abcfnrtv", *p))
|
||||
print_esc_char(*p++);
|
||||
else if (*p == L'u' || *p == L'U')
|
||||
{
|
||||
wchar_t esc_char = *p;
|
||||
p++;
|
||||
uint32_t uni_value = 0;
|
||||
for (size_t esc_length = 0; esc_length < (esc_char == L'u' ? 4 : 8); esc_length++)
|
||||
{
|
||||
if (! is_hex_digit(*p))
|
||||
{
|
||||
/* Escape sequence must be done. Complain if we didn't get anything */
|
||||
if (esc_length == 0)
|
||||
{
|
||||
this->fatal_error(_(L"Missing hexadecimal number in Unicode escape"));
|
||||
}
|
||||
break;
|
||||
}
|
||||
uni_value = uni_value * 16 + hex_to_bin(*p);
|
||||
p++;
|
||||
}
|
||||
|
||||
/* PCA GNU printf respects the limitations described in ISO N717, about which universal characters "shall not" be specified. I believe this limitation is for the benefit of compilers; I see no reason to impose it in builtin_printf.
|
||||
|
||||
If __STDC_ISO_10646__ is defined, then it means wchar_t can and does hold Unicode code points, so just use that. If not defined, use the %lc printf conversion; this probably won't do anything good if your wide character set is not Unicode, but such platforms are exceedingly rare.
|
||||
*/
|
||||
if (uni_value > 0x10FFFF)
|
||||
{
|
||||
this->fatal_error(_(L"Unicode character out of range: \\%c%0*x"), esc_char, (esc_char == L'u' ? 4 : 8), uni_value);
|
||||
}
|
||||
else
|
||||
{
|
||||
#if defined(__STDC_ISO_10646__)
|
||||
this->append_output(uni_value);
|
||||
#else
|
||||
this->append_format_output(L"%lc", uni_value);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this->append_output(L'\\');
|
||||
if (*p)
|
||||
{
|
||||
this->append_output(*p);
|
||||
p++;
|
||||
}
|
||||
}
|
||||
return p - escstart - 1;
|
||||
}
|
||||
|
||||
/* Print string STR, evaluating \ escapes. */
|
||||
|
||||
void builtin_printf_state_t::print_esc_string(const wchar_t *str)
|
||||
{
|
||||
for (; *str; str++)
|
||||
if (*str == L'\\')
|
||||
str += print_esc(str, true);
|
||||
else
|
||||
this->append_output(*str);
|
||||
}
|
||||
|
||||
/* Evaluate a printf conversion specification. START is the start of
|
||||
the directive, LENGTH is its length, and CONVERSION specifies the
|
||||
type of conversion. LENGTH does not include any length modifier or
|
||||
the conversion specifier itself. FIELD_WIDTH and PRECISION are the
|
||||
field width and precision for '*' values, if HAVE_FIELD_WIDTH and
|
||||
HAVE_PRECISION are true, respectively. ARGUMENT is the argument to
|
||||
be formatted. */
|
||||
|
||||
void builtin_printf_state_t::print_direc(const wchar_t *start, size_t length, wchar_t conversion,
|
||||
bool have_field_width, int field_width,
|
||||
bool have_precision, int precision,
|
||||
wchar_t const *argument)
|
||||
{
|
||||
// Start with everything except the conversion specifier
|
||||
wcstring fmt(start, length);
|
||||
|
||||
/* Create a copy of the % directive, with an intmax_t-wide width modifier substituted for any existing integer length modifier. */
|
||||
switch (conversion)
|
||||
{
|
||||
case L'd':
|
||||
case L'i':
|
||||
case L'u':
|
||||
fmt.append(L"ll");
|
||||
break;
|
||||
case L'a':
|
||||
case L'e':
|
||||
case L'f':
|
||||
case L'g':
|
||||
case L'A':
|
||||
case L'E':
|
||||
case L'F':
|
||||
case L'G':
|
||||
fmt.append(L"L");
|
||||
break;
|
||||
case L's':
|
||||
fmt.append(L"l");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Append the conversion itself
|
||||
fmt.push_back(conversion);
|
||||
|
||||
switch (conversion)
|
||||
{
|
||||
case L'd':
|
||||
case L'i':
|
||||
{
|
||||
intmax_t arg = string_to_scalar_type<intmax_t>(argument, this);
|
||||
if (! have_field_width)
|
||||
{
|
||||
if (! have_precision)
|
||||
this->append_format_output(fmt.c_str(), arg);
|
||||
else
|
||||
this->append_format_output(fmt.c_str(), precision, arg);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (! have_precision)
|
||||
this->append_format_output(fmt.c_str(), field_width, arg);
|
||||
else
|
||||
this->append_format_output(fmt.c_str(), field_width, precision, arg);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case L'o':
|
||||
case L'u':
|
||||
case L'x':
|
||||
case L'X':
|
||||
{
|
||||
uintmax_t arg = string_to_scalar_type<uintmax_t>(argument, this);
|
||||
if (!have_field_width)
|
||||
{
|
||||
if (!have_precision)
|
||||
this->append_format_output(fmt.c_str(), arg);
|
||||
else
|
||||
this->append_format_output(fmt.c_str(), precision, arg);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!have_precision)
|
||||
this->append_format_output(fmt.c_str(), field_width, arg);
|
||||
else
|
||||
this->append_format_output(fmt.c_str(), field_width, precision, arg);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case L'a':
|
||||
case L'A':
|
||||
case L'e':
|
||||
case L'E':
|
||||
case L'f':
|
||||
case L'F':
|
||||
case L'g':
|
||||
case L'G':
|
||||
{
|
||||
long double arg = string_to_scalar_type<long double>(argument, this);
|
||||
if (!have_field_width)
|
||||
{
|
||||
if (!have_precision)
|
||||
this->append_format_output(fmt.c_str(), arg);
|
||||
else
|
||||
this->append_format_output(fmt.c_str(), precision, arg);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!have_precision)
|
||||
this->append_format_output(fmt.c_str(), field_width, arg);
|
||||
else
|
||||
this->append_format_output(fmt.c_str(), field_width, precision, arg);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case L'c':
|
||||
if (!have_field_width)
|
||||
this->append_format_output(fmt.c_str(), *argument);
|
||||
else
|
||||
this->append_format_output(fmt.c_str(), field_width, *argument);
|
||||
break;
|
||||
case L's':
|
||||
if (!have_field_width)
|
||||
{
|
||||
if (!have_precision)
|
||||
{
|
||||
this->append_format_output(fmt.c_str(), argument);
|
||||
}
|
||||
else
|
||||
this->append_format_output(fmt.c_str(), precision, argument);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!have_precision)
|
||||
this->append_format_output(fmt.c_str(), field_width, argument);
|
||||
else
|
||||
this->append_format_output(fmt.c_str(), field_width, precision, argument);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Print the text in FORMAT, using ARGV (with ARGC elements) for
|
||||
arguments to any `%' directives.
|
||||
Return the number of elements of ARGV used. */
|
||||
|
||||
int builtin_printf_state_t::print_formatted(const wchar_t *format, int argc, wchar_t **argv)
|
||||
{
|
||||
int save_argc = argc; /* Preserve original value. */
|
||||
const wchar_t *f; /* Pointer into `format'. */
|
||||
const wchar_t *direc_start; /* Start of % directive. */
|
||||
size_t direc_length; /* Length of % directive. */
|
||||
bool have_field_width; /* True if FIELD_WIDTH is valid. */
|
||||
int field_width = 0; /* Arg to first '*'. */
|
||||
bool have_precision; /* True if PRECISION is valid. */
|
||||
int precision = 0; /* Arg to second '*'. */
|
||||
bool ok[UCHAR_MAX + 1] = { }; /* ok['x'] is true if %x is allowed. */
|
||||
|
||||
for (f = format; *f != L'\0'; ++f)
|
||||
{
|
||||
switch (*f)
|
||||
{
|
||||
case L'%':
|
||||
direc_start = f++;
|
||||
direc_length = 1;
|
||||
have_field_width = have_precision = false;
|
||||
if (*f == L'%')
|
||||
{
|
||||
this->append_output(L'%');
|
||||
break;
|
||||
}
|
||||
if (*f == L'b')
|
||||
{
|
||||
/* FIXME: Field width and precision are not supported
|
||||
for %b, even though POSIX requires it. */
|
||||
if (argc > 0)
|
||||
{
|
||||
print_esc_string(*argv);
|
||||
++argv;
|
||||
--argc;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
ok['a'] = ok['A'] = ok['c'] = ok['d'] = ok['e'] = ok['E'] =
|
||||
ok['f'] = ok['F'] = ok['g'] = ok['G'] = ok['i'] = ok['o'] =
|
||||
ok['s'] = ok['u'] = ok['x'] = ok['X'] = true;
|
||||
|
||||
for (;; f++, direc_length++)
|
||||
{
|
||||
switch (*f)
|
||||
{
|
||||
case L'I':
|
||||
case L'\'':
|
||||
ok['a'] = ok['A'] = ok['c'] = ok['e'] = ok['E'] =
|
||||
ok['o'] = ok['s'] = ok['x'] = ok['X'] = false;
|
||||
break;
|
||||
case '-':
|
||||
case '+':
|
||||
case ' ':
|
||||
break;
|
||||
case L'#':
|
||||
ok['c'] = ok['d'] = ok['i'] = ok['s'] = ok['u'] = false;
|
||||
break;
|
||||
case '0':
|
||||
ok['c'] = ok['s'] = false;
|
||||
break;
|
||||
default:
|
||||
goto no_more_flag_characters;
|
||||
}
|
||||
}
|
||||
no_more_flag_characters:
|
||||
;
|
||||
|
||||
if (*f == L'*')
|
||||
{
|
||||
++f;
|
||||
++direc_length;
|
||||
if (argc > 0)
|
||||
{
|
||||
intmax_t width = string_to_scalar_type<intmax_t>(*argv, this);
|
||||
if (INT_MIN <= width && width <= INT_MAX)
|
||||
field_width = static_cast<int>(width);
|
||||
else
|
||||
this->fatal_error(_(L"invalid field width: %ls"), *argv);
|
||||
++argv;
|
||||
--argc;
|
||||
}
|
||||
else
|
||||
{
|
||||
field_width = 0;
|
||||
}
|
||||
have_field_width = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
while (iswdigit(*f))
|
||||
{
|
||||
++f;
|
||||
++direc_length;
|
||||
}
|
||||
}
|
||||
if (*f == L'.')
|
||||
{
|
||||
++f;
|
||||
++direc_length;
|
||||
ok['c'] = false;
|
||||
if (*f == L'*')
|
||||
{
|
||||
++f;
|
||||
++direc_length;
|
||||
if (argc > 0)
|
||||
{
|
||||
intmax_t prec = string_to_scalar_type<intmax_t>(*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. */
|
||||
precision = -1;
|
||||
}
|
||||
else if (INT_MAX < prec)
|
||||
this->fatal_error(_(L"invalid precision: %ls"), *argv);
|
||||
else
|
||||
{
|
||||
precision = static_cast<int>(prec);
|
||||
}
|
||||
++argv;
|
||||
--argc;
|
||||
}
|
||||
else
|
||||
{
|
||||
precision = 0;
|
||||
}
|
||||
have_precision = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
while (iswdigit(*f))
|
||||
{
|
||||
++f;
|
||||
++direc_length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (*f == L'l' || *f == L'L' || *f == L'h' || *f == L'j' || *f == L't' || *f == L'z')
|
||||
++f;
|
||||
|
||||
{
|
||||
unsigned wchar_t conversion = *f;
|
||||
if (! ok[conversion])
|
||||
{
|
||||
this->fatal_error(_(L"%.*ls: invalid conversion specification"), (int)(f + 1 - direc_start), direc_start);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
print_direc(direc_start, direc_length, *f,
|
||||
have_field_width, field_width,
|
||||
have_precision, precision,
|
||||
(argc <= 0 ? L"" : (argc--, *argv++)));
|
||||
break;
|
||||
|
||||
case L'\\':
|
||||
f += print_esc(f, false);
|
||||
break;
|
||||
|
||||
default:
|
||||
this->append_output(*f);
|
||||
}
|
||||
}
|
||||
return save_argc - argc;
|
||||
}
|
||||
|
||||
static int builtin_printf(parser_t &parser, wchar_t **argv)
|
||||
{
|
||||
builtin_printf_state_t state;
|
||||
|
||||
wchar_t *format;
|
||||
int args_used;
|
||||
int argc = builtin_count_args(argv);
|
||||
|
||||
if (argc <= 1)
|
||||
{
|
||||
state.fatal_error(_(L"printf: not enough arguments"));
|
||||
return STATUS_BUILTIN_ERROR;
|
||||
}
|
||||
|
||||
format = argv[1];
|
||||
argc -= 2;
|
||||
argv += 2;
|
||||
|
||||
do
|
||||
{
|
||||
args_used = state.print_formatted(format, argc, argv);
|
||||
argc -= args_used;
|
||||
argv += args_used;
|
||||
}
|
||||
while (args_used > 0 && argc > 0 && ! state.early_exit);
|
||||
return state.exit_code;
|
||||
}
|
||||
811
builtin_set.cpp
Normal file
811
builtin_set.cpp
Normal file
@@ -0,0 +1,811 @@
|
||||
/** \file builtin_set.c Functions defining the set builtin
|
||||
|
||||
Functions used for implementing the set builtin.
|
||||
|
||||
*/
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <wchar.h>
|
||||
#include <wctype.h>
|
||||
#include <sys/types.h>
|
||||
#include <termios.h>
|
||||
#include <signal.h>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include "fallback.h"
|
||||
#include "util.h"
|
||||
|
||||
#include "wutil.h"
|
||||
#include "builtin.h"
|
||||
#include "env.h"
|
||||
#include "expand.h"
|
||||
#include "common.h"
|
||||
#include "wgetopt.h"
|
||||
#include "proc.h"
|
||||
#include "parser.h"
|
||||
|
||||
/* We know about these buffers */
|
||||
extern wcstring stdout_buffer, stderr_buffer;
|
||||
|
||||
/**
|
||||
Error message for invalid path operations
|
||||
*/
|
||||
#define BUILTIN_SET_PATH_ERROR L"%ls: Warning: path component %ls may not be valid in %ls.\n"
|
||||
|
||||
/**
|
||||
Hint for invalid path operation with a colon
|
||||
*/
|
||||
#define BUILTIN_SET_PATH_HINT L"%ls: Did you mean 'set %ls $%ls %ls'?\n"
|
||||
|
||||
/**
|
||||
Error for mismatch between index count and elements
|
||||
*/
|
||||
#define BUILTIN_SET_ARG_COUNT L"%ls: The number of variable indexes does not match the number of values\n"
|
||||
|
||||
/**
|
||||
Test if the specified variable should be subject to path validation
|
||||
*/
|
||||
static int is_path_variable(const wchar_t *env)
|
||||
{
|
||||
return contains(env, L"PATH", L"CDPATH");
|
||||
}
|
||||
|
||||
/**
|
||||
Call env_set. If this is a path variable, e.g. PATH, validate the
|
||||
elements. On error, print a description of the problem to stderr.
|
||||
*/
|
||||
static int my_env_set(const wchar_t *key, const wcstring_list_t &val, int scope)
|
||||
{
|
||||
size_t i;
|
||||
int retcode = 0;
|
||||
const wchar_t *val_str=NULL;
|
||||
|
||||
if (is_path_variable(key))
|
||||
{
|
||||
/* Fix for https://github.com/fish-shell/fish-shell/issues/199 . Return success if any path setting succeeds. */
|
||||
bool any_success = false;
|
||||
|
||||
/* Don't bother validating (or complaining about) values that are already present */
|
||||
wcstring_list_t existing_values;
|
||||
const env_var_t existing_variable = env_get_string(key);
|
||||
if (! existing_variable.missing_or_empty())
|
||||
tokenize_variable_array(existing_variable, existing_values);
|
||||
|
||||
for (i=0; i< val.size() ; i++)
|
||||
{
|
||||
const wcstring &dir = val.at(i);
|
||||
if (list_contains_string(existing_values, dir))
|
||||
{
|
||||
any_success = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
bool show_perror = false;
|
||||
int show_hint = 0;
|
||||
bool error = false;
|
||||
|
||||
struct stat buff;
|
||||
if (wstat(dir, &buff))
|
||||
{
|
||||
error = true;
|
||||
show_perror = true;
|
||||
}
|
||||
|
||||
if (!(S_ISDIR(buff.st_mode)))
|
||||
{
|
||||
error = true;
|
||||
}
|
||||
|
||||
if (!error)
|
||||
{
|
||||
any_success = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
append_format(stderr_buffer, _(BUILTIN_SET_PATH_ERROR), L"set", dir.c_str(), key);
|
||||
const wchar_t *colon = wcschr(dir.c_str(), L':');
|
||||
|
||||
if (colon && *(colon+1))
|
||||
{
|
||||
show_hint = 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (show_perror)
|
||||
{
|
||||
builtin_wperror(L"set");
|
||||
}
|
||||
|
||||
if (show_hint)
|
||||
{
|
||||
append_format(stderr_buffer, _(BUILTIN_SET_PATH_HINT), L"set", key, key, wcschr(dir.c_str(), L':')+1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Fail at setting the path if we tried to set it to something non-empty, but it wound up empty. */
|
||||
if (! val.empty() && ! any_success)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
wcstring sb;
|
||||
if (! val.empty())
|
||||
{
|
||||
for (i=0; i< val.size() ; i++)
|
||||
{
|
||||
sb.append(val[i]);
|
||||
if (i<val.size() - 1)
|
||||
{
|
||||
sb.append(ARRAY_SEP_STR);
|
||||
}
|
||||
}
|
||||
val_str = sb.c_str();
|
||||
}
|
||||
|
||||
switch (env_set(key, val_str, scope | ENV_USER))
|
||||
{
|
||||
case ENV_PERM:
|
||||
{
|
||||
append_format(stderr_buffer, _(L"%ls: Tried to change the read-only variable '%ls'\n"), L"set", key);
|
||||
retcode=1;
|
||||
break;
|
||||
}
|
||||
|
||||
case ENV_INVALID:
|
||||
{
|
||||
append_format(stderr_buffer, _(L"%ls: Unknown error"), L"set");
|
||||
retcode=1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return retcode;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
Extract indexes from a destination argument of the form name[index1 index2...]
|
||||
|
||||
\param indexes the list to insert the new indexes into
|
||||
\param src the source string to parse
|
||||
\param name the name of the element. Return null if the name in \c src does not match this name
|
||||
\param var_count the number of elements in the array to parse.
|
||||
|
||||
\return the total number of indexes parsed, or -1 on error
|
||||
*/
|
||||
static int parse_index(std::vector<long> &indexes,
|
||||
const wchar_t *src,
|
||||
const wchar_t *name,
|
||||
size_t var_count)
|
||||
{
|
||||
size_t len;
|
||||
|
||||
int count = 0;
|
||||
const wchar_t *src_orig = src;
|
||||
|
||||
if (src == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (*src != L'\0' && (iswalnum(*src) || *src == L'_'))
|
||||
{
|
||||
src++;
|
||||
}
|
||||
|
||||
if (*src != L'[')
|
||||
{
|
||||
append_format(stderr_buffer, _(BUILTIN_SET_ARG_COUNT), L"set");
|
||||
return 0;
|
||||
}
|
||||
|
||||
len = src-src_orig;
|
||||
|
||||
if ((wcsncmp(src_orig, name, len)!=0) || (wcslen(name) != (len)))
|
||||
{
|
||||
append_format(stderr_buffer,
|
||||
_(L"%ls: Multiple variable names specified in single call (%ls and %.*ls)\n"),
|
||||
L"set",
|
||||
name,
|
||||
len,
|
||||
src_orig);
|
||||
return 0;
|
||||
}
|
||||
|
||||
src++;
|
||||
|
||||
while (iswspace(*src))
|
||||
{
|
||||
src++;
|
||||
}
|
||||
|
||||
while (*src != L']')
|
||||
{
|
||||
wchar_t *end;
|
||||
|
||||
long l_ind;
|
||||
|
||||
errno = 0;
|
||||
|
||||
l_ind = wcstol(src, &end, 10);
|
||||
|
||||
if (end==src || errno)
|
||||
{
|
||||
append_format(stderr_buffer, _(L"%ls: Invalid index starting at '%ls'\n"), L"set", src);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (l_ind < 0)
|
||||
{
|
||||
l_ind = var_count+l_ind+1;
|
||||
}
|
||||
|
||||
src = end;
|
||||
if (*src==L'.' && *(src+1)==L'.')
|
||||
{
|
||||
src+=2;
|
||||
long l_ind2 = wcstol(src, &end, 10);
|
||||
if (end==src || errno)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
src = end;
|
||||
|
||||
if (l_ind2 < 0)
|
||||
{
|
||||
l_ind2 = var_count+l_ind2+1;
|
||||
}
|
||||
int direction = l_ind2<l_ind ? -1 : 1 ;
|
||||
for (long jjj = l_ind; jjj*direction <= l_ind2*direction; jjj+=direction)
|
||||
{
|
||||
// debug(0, L"Expand range [set]: %i\n", jjj);
|
||||
indexes.push_back(jjj);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
indexes.push_back(l_ind);
|
||||
count++;
|
||||
}
|
||||
while (iswspace(*src)) src++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static int update_values(wcstring_list_t &list,
|
||||
std::vector<long> &indexes,
|
||||
wcstring_list_t &values)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
/* Replace values where needed */
|
||||
for (i = 0; i < indexes.size(); i++)
|
||||
{
|
||||
/*
|
||||
The '- 1' below is because the indices in fish are
|
||||
one-based, but the vector uses zero-based indices
|
||||
*/
|
||||
long ind = indexes[i] - 1;
|
||||
const wcstring newv = values[ i ];
|
||||
if (ind < 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if ((size_t)ind >= list.size())
|
||||
{
|
||||
list.resize(ind+1);
|
||||
}
|
||||
|
||||
// free((void *) al_get(list, ind));
|
||||
list[ ind ] = newv;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
Erase from a list of wcstring values at specified indexes
|
||||
*/
|
||||
static void erase_values(wcstring_list_t &list, const std::vector<long> &indexes)
|
||||
{
|
||||
// Make a set of indexes.
|
||||
// This both sorts them into ascending order and removes duplicates.
|
||||
const std::set<long> indexes_set(indexes.begin(), indexes.end());
|
||||
|
||||
// Now walk the set backwards, so we encounter larger indexes first, and remove elements at the given (1-based) indexes.
|
||||
std::set<long>::const_reverse_iterator iter;
|
||||
for (iter = indexes_set.rbegin(); iter != indexes_set.rend(); ++iter)
|
||||
{
|
||||
long val = *iter;
|
||||
if (val > 0 && (size_t)val <= list.size())
|
||||
{
|
||||
// One-based indexing!
|
||||
list.erase(list.begin() + val - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Print the names of all environment variables in the scope, with or without shortening,
|
||||
with or without values, with or without escaping
|
||||
*/
|
||||
static void print_variables(int include_values, int esc, bool shorten_ok, int scope)
|
||||
{
|
||||
wcstring_list_t names = env_get_names(scope);
|
||||
sort(names.begin(), names.end());
|
||||
|
||||
for (size_t i = 0; i < names.size(); i++)
|
||||
{
|
||||
const wcstring key = names.at(i);
|
||||
const wcstring e_key = escape_string(key, 0);
|
||||
|
||||
stdout_buffer.append(e_key);
|
||||
|
||||
if (include_values)
|
||||
{
|
||||
env_var_t value = env_get_string(key);
|
||||
if (!value.missing())
|
||||
{
|
||||
int shorten = 0;
|
||||
|
||||
if (shorten_ok && value.length() > 64)
|
||||
{
|
||||
shorten = 1;
|
||||
value.resize(60);
|
||||
}
|
||||
|
||||
wcstring e_value = esc ? expand_escape_variable(value) : value;
|
||||
|
||||
stdout_buffer.append(L" ");
|
||||
stdout_buffer.append(e_value);
|
||||
|
||||
if (shorten)
|
||||
{
|
||||
stdout_buffer.push_back(ellipsis_char);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
stdout_buffer.append(L"\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
The set builtin. Creates, updates and erases environment variables
|
||||
and environemnt variable arrays.
|
||||
*/
|
||||
static int builtin_set(parser_t &parser, wchar_t **argv)
|
||||
{
|
||||
/** Variables used for parsing the argument list */
|
||||
const struct woption long_options[] =
|
||||
{
|
||||
{ L"export", no_argument, 0, 'x' },
|
||||
{ L"global", no_argument, 0, 'g' },
|
||||
{ L"local", no_argument, 0, 'l' },
|
||||
{ L"erase", no_argument, 0, 'e' },
|
||||
{ L"names", no_argument, 0, 'n' },
|
||||
{ L"unexport", no_argument, 0, 'u' },
|
||||
{ L"universal", no_argument, 0, 'U' },
|
||||
{ L"long", no_argument, 0, 'L' },
|
||||
{ L"query", no_argument, 0, 'q' },
|
||||
{ L"help", no_argument, 0, 'h' },
|
||||
{ 0, 0, 0, 0 }
|
||||
} ;
|
||||
|
||||
const wchar_t *short_options = L"+xglenuULqh";
|
||||
|
||||
int argc = builtin_count_args(argv);
|
||||
|
||||
/*
|
||||
Flags to set the work mode
|
||||
*/
|
||||
int local = 0, global = 0, exportv = 0;
|
||||
int erase = 0, list = 0, unexport=0;
|
||||
int universal = 0, query=0;
|
||||
bool shorten_ok = true;
|
||||
bool preserve_incoming_failure_exit_status = true;
|
||||
const int incoming_exit_status = proc_get_last_status();
|
||||
|
||||
/*
|
||||
Variables used for performing the actual work
|
||||
*/
|
||||
wchar_t *dest = 0;
|
||||
int retcode=0;
|
||||
int scope;
|
||||
int slice=0;
|
||||
int i;
|
||||
|
||||
wchar_t *bad_char;
|
||||
|
||||
|
||||
/* Parse options to obtain the requested operation and the modifiers */
|
||||
woptind = 0;
|
||||
while (1)
|
||||
{
|
||||
int c = wgetopt_long(argc, argv, short_options, long_options, 0);
|
||||
|
||||
if (c == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case 0:
|
||||
break;
|
||||
|
||||
case 'e':
|
||||
erase = 1;
|
||||
preserve_incoming_failure_exit_status = false;
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
list = 1;
|
||||
preserve_incoming_failure_exit_status = false;
|
||||
break;
|
||||
|
||||
case 'x':
|
||||
exportv = 1;
|
||||
break;
|
||||
|
||||
case 'l':
|
||||
local = 1;
|
||||
break;
|
||||
|
||||
case 'g':
|
||||
global = 1;
|
||||
break;
|
||||
|
||||
case 'u':
|
||||
unexport = 1;
|
||||
break;
|
||||
|
||||
case 'U':
|
||||
universal = 1;
|
||||
break;
|
||||
|
||||
case 'L':
|
||||
shorten_ok = false;
|
||||
break;
|
||||
|
||||
case 'q':
|
||||
query = 1;
|
||||
preserve_incoming_failure_exit_status = false;
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
builtin_print_help(parser, argv[0], stdout_buffer);
|
||||
return 0;
|
||||
|
||||
case '?':
|
||||
builtin_unknown_option(parser, argv[0], argv[woptind-1]);
|
||||
return 1;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Ok, all arguments have been parsed, let's validate them
|
||||
*/
|
||||
|
||||
/*
|
||||
If we are checking the existance of a variable (-q) we can not
|
||||
also specify scope
|
||||
*/
|
||||
|
||||
if (query && (erase || list))
|
||||
{
|
||||
append_format(stderr_buffer,
|
||||
BUILTIN_ERR_COMBO,
|
||||
argv[0]);
|
||||
|
||||
builtin_print_help(parser, argv[0], stderr_buffer);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* We can't both list and erase varaibles */
|
||||
if (erase && list)
|
||||
{
|
||||
append_format(stderr_buffer,
|
||||
BUILTIN_ERR_COMBO,
|
||||
argv[0]);
|
||||
|
||||
builtin_print_help(parser, argv[0], stderr_buffer);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
Variables can only have one scope
|
||||
*/
|
||||
if (local + global + universal > 1)
|
||||
{
|
||||
append_format(stderr_buffer,
|
||||
BUILTIN_ERR_GLOCAL,
|
||||
argv[0]);
|
||||
builtin_print_help(parser, argv[0], stderr_buffer);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
Variables can only have one export status
|
||||
*/
|
||||
if (exportv && unexport)
|
||||
{
|
||||
append_format(stderr_buffer,
|
||||
BUILTIN_ERR_EXPUNEXP,
|
||||
argv[0]);
|
||||
builtin_print_help(parser, argv[0], stderr_buffer);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
Calculate the scope value for variable assignement
|
||||
*/
|
||||
scope = (local ? ENV_LOCAL : 0) | (global ? ENV_GLOBAL : 0) | (exportv ? ENV_EXPORT : 0) | (unexport ? ENV_UNEXPORT : 0) | (universal ? ENV_UNIVERSAL:0) | ENV_USER;
|
||||
|
||||
if (query)
|
||||
{
|
||||
/*
|
||||
Query mode. Return the number of variables that do not exist
|
||||
out of the specified variables.
|
||||
*/
|
||||
int i;
|
||||
for (i=woptind; i<argc; i++)
|
||||
{
|
||||
wchar_t *arg = argv[i];
|
||||
int slice=0;
|
||||
|
||||
if (!(dest = wcsdup(arg)))
|
||||
{
|
||||
DIE_MEM();
|
||||
}
|
||||
|
||||
if (wcschr(dest, L'['))
|
||||
{
|
||||
slice = 1;
|
||||
*wcschr(dest, L'[')=0;
|
||||
}
|
||||
|
||||
if (slice)
|
||||
{
|
||||
std::vector<long> indexes;
|
||||
wcstring_list_t result;
|
||||
size_t j;
|
||||
|
||||
env_var_t dest_str = env_get_string(dest);
|
||||
if (! dest_str.missing())
|
||||
tokenize_variable_array(dest_str, result);
|
||||
|
||||
if (!parse_index(indexes, arg, dest, result.size()))
|
||||
{
|
||||
builtin_print_help(parser, argv[0], stderr_buffer);
|
||||
retcode = 1;
|
||||
break;
|
||||
}
|
||||
for (j=0; j < indexes.size() ; j++)
|
||||
{
|
||||
long idx = indexes[j];
|
||||
if (idx < 1 || (size_t)idx > result.size())
|
||||
{
|
||||
retcode++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!env_exist(arg, scope))
|
||||
{
|
||||
retcode++;
|
||||
}
|
||||
}
|
||||
|
||||
free(dest);
|
||||
|
||||
}
|
||||
return retcode;
|
||||
}
|
||||
|
||||
if (list)
|
||||
{
|
||||
/* Maybe we should issue an error if there are any other arguments? */
|
||||
print_variables(0, 0, shorten_ok, scope);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (woptind == argc)
|
||||
{
|
||||
/*
|
||||
Print values of variables
|
||||
*/
|
||||
|
||||
if (erase)
|
||||
{
|
||||
append_format(stderr_buffer,
|
||||
_(L"%ls: Erase needs a variable name\n"),
|
||||
argv[0]);
|
||||
|
||||
builtin_print_help(parser, argv[0], stderr_buffer);
|
||||
retcode = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
print_variables(1, 1, shorten_ok, scope);
|
||||
}
|
||||
|
||||
return retcode;
|
||||
}
|
||||
|
||||
if (!(dest = wcsdup(argv[woptind])))
|
||||
{
|
||||
DIE_MEM();
|
||||
}
|
||||
|
||||
if (wcschr(dest, L'['))
|
||||
{
|
||||
slice = 1;
|
||||
*wcschr(dest, L'[')=0;
|
||||
}
|
||||
|
||||
if (!wcslen(dest))
|
||||
{
|
||||
free(dest);
|
||||
append_format(stderr_buffer, BUILTIN_ERR_VARNAME_ZERO, argv[0]);
|
||||
builtin_print_help(parser, argv[0], stderr_buffer);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ((bad_char = wcsvarname(dest)))
|
||||
{
|
||||
append_format(stderr_buffer, BUILTIN_ERR_VARCHAR, argv[0], *bad_char);
|
||||
builtin_print_help(parser, argv[0], stderr_buffer);
|
||||
free(dest);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (slice && erase && (scope != ENV_USER))
|
||||
{
|
||||
free(dest);
|
||||
append_format(stderr_buffer, _(L"%ls: Can not specify scope when erasing array slice\n"), argv[0]);
|
||||
builtin_print_help(parser, argv[0], stderr_buffer);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
set assignment can work in two modes, either using slices or
|
||||
using the whole array. We detect which mode is used here.
|
||||
*/
|
||||
|
||||
if (slice)
|
||||
{
|
||||
|
||||
/*
|
||||
Slice mode
|
||||
*/
|
||||
size_t idx_count, val_count;
|
||||
wcstring_list_t values;
|
||||
std::vector<long> indexes;
|
||||
wcstring_list_t result;
|
||||
|
||||
const env_var_t dest_str = env_get_string(dest);
|
||||
if (! dest_str.missing())
|
||||
tokenize_variable_array(dest_str, result);
|
||||
|
||||
for (; woptind<argc; woptind++)
|
||||
{
|
||||
if (!parse_index(indexes, argv[woptind], dest, result.size()))
|
||||
{
|
||||
builtin_print_help(parser, argv[0], stderr_buffer);
|
||||
retcode = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
val_count = argc-woptind-1;
|
||||
idx_count = indexes.size();
|
||||
|
||||
if (!erase)
|
||||
{
|
||||
if (val_count < idx_count)
|
||||
{
|
||||
append_format(stderr_buffer, _(BUILTIN_SET_ARG_COUNT), argv[0]);
|
||||
builtin_print_help(parser, argv[0], stderr_buffer);
|
||||
retcode=1;
|
||||
break;
|
||||
}
|
||||
if (val_count == idx_count)
|
||||
{
|
||||
woptind++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!retcode)
|
||||
{
|
||||
/*
|
||||
Slice indexes have been calculated, do the actual work
|
||||
*/
|
||||
|
||||
if (erase)
|
||||
{
|
||||
erase_values(result, indexes);
|
||||
my_env_set(dest, result, scope);
|
||||
}
|
||||
else
|
||||
{
|
||||
wcstring_list_t value;
|
||||
|
||||
while (woptind < argc)
|
||||
{
|
||||
value.push_back(argv[woptind++]);
|
||||
}
|
||||
|
||||
if (update_values(result,
|
||||
indexes,
|
||||
value))
|
||||
{
|
||||
append_format(stderr_buffer, L"%ls: ", argv[0]);
|
||||
append_format(stderr_buffer, ARRAY_BOUNDS_ERR);
|
||||
stderr_buffer.push_back(L'\n');
|
||||
}
|
||||
|
||||
my_env_set(dest, result, scope);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
woptind++;
|
||||
|
||||
/*
|
||||
No slicing
|
||||
*/
|
||||
if (erase)
|
||||
{
|
||||
if (woptind != argc)
|
||||
{
|
||||
append_format(stderr_buffer,
|
||||
_(L"%ls: Values cannot be specfied with erase\n"),
|
||||
argv[0]);
|
||||
builtin_print_help(parser, argv[0], stderr_buffer);
|
||||
retcode=1;
|
||||
}
|
||||
else
|
||||
{
|
||||
retcode = env_remove(dest, scope);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
wcstring_list_t val;
|
||||
for (i=woptind; i<argc; i++)
|
||||
val.push_back(argv[i]);
|
||||
retcode = my_env_set(dest, val, scope);
|
||||
}
|
||||
}
|
||||
|
||||
free(dest);
|
||||
|
||||
if (retcode == STATUS_BUILTIN_OK && preserve_incoming_failure_exit_status)
|
||||
retcode = incoming_exit_status;
|
||||
return retcode;
|
||||
|
||||
}
|
||||
|
||||
243
builtin_set_color.cpp
Normal file
243
builtin_set_color.cpp
Normal file
@@ -0,0 +1,243 @@
|
||||
/** \file builtin_set_color.cpp Functions defining the set_color builtin
|
||||
|
||||
Functions used for implementing the set_color builtin.
|
||||
|
||||
*/
|
||||
#include "config.h"
|
||||
|
||||
#include "builtin.h"
|
||||
#include "color.h"
|
||||
#include "output.h"
|
||||
|
||||
#if HAVE_NCURSES_H
|
||||
#include <ncurses.h>
|
||||
#else
|
||||
#include <curses.h>
|
||||
#endif
|
||||
|
||||
#if HAVE_TERM_H
|
||||
#include <term.h>
|
||||
#elif HAVE_NCURSES_TERM_H
|
||||
#include <ncurses/term.h>
|
||||
#endif
|
||||
|
||||
|
||||
/* We know about these buffers */
|
||||
extern wcstring stdout_buffer, stderr_buffer;
|
||||
|
||||
/**
|
||||
Error message for invalid path operations
|
||||
*/
|
||||
#define BUILTIN_SET_PATH_ERROR L"%ls: Warning: path component %ls may not be valid in %ls.\n"
|
||||
|
||||
/**
|
||||
Hint for invalid path operation with a colon
|
||||
*/
|
||||
#define BUILTIN_SET_PATH_HINT L"%ls: Did you mean 'set %ls $%ls %ls'?\n"
|
||||
|
||||
/**
|
||||
Error for mismatch between index count and elements
|
||||
*/
|
||||
#define BUILTIN_SET_ARG_COUNT L"%ls: The number of variable indexes does not match the number of values\n"
|
||||
|
||||
static void print_colors(void)
|
||||
{
|
||||
const wcstring_list_t result = rgb_color_t::named_color_names();
|
||||
size_t i;
|
||||
for (i=0; i < result.size(); i++)
|
||||
{
|
||||
stdout_buffer.append(result.at(i));
|
||||
stdout_buffer.push_back(L'\n');
|
||||
}
|
||||
}
|
||||
|
||||
/* function we set as the output writer */
|
||||
static std::string builtin_set_color_output;
|
||||
static int set_color_builtin_outputter(char c)
|
||||
{
|
||||
ASSERT_IS_MAIN_THREAD();
|
||||
builtin_set_color_output.push_back(c);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
set_color builtin
|
||||
*/
|
||||
static int builtin_set_color(parser_t &parser, wchar_t **argv)
|
||||
{
|
||||
/** Variables used for parsing the argument list */
|
||||
const struct woption long_options[] =
|
||||
{
|
||||
{ L"background", required_argument, 0, 'b'},
|
||||
{ L"help", no_argument, 0, 'h' },
|
||||
{ L"bold", no_argument, 0, 'o' },
|
||||
{ L"underline", no_argument, 0, 'u' },
|
||||
{ L"version", no_argument, 0, 'v' },
|
||||
{ L"print-colors", no_argument, 0, 'c' },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
const wchar_t *short_options = L"b:hvocu";
|
||||
|
||||
int argc = builtin_count_args(argv);
|
||||
|
||||
const wchar_t *bgcolor = NULL;
|
||||
bool bold = false, underline=false;
|
||||
int errret;
|
||||
|
||||
/* Parse options to obtain the requested operation and the modifiers */
|
||||
woptind = 0;
|
||||
while (1)
|
||||
{
|
||||
int c = wgetopt_long(argc, argv, short_options, long_options, 0);
|
||||
|
||||
if (c == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case 0:
|
||||
break;
|
||||
|
||||
case 'b':
|
||||
bgcolor = woptarg;
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
builtin_print_help(parser, argv[0], stdout_buffer);
|
||||
return STATUS_BUILTIN_OK;
|
||||
|
||||
case 'o':
|
||||
bold = true;
|
||||
break;
|
||||
|
||||
case 'u':
|
||||
underline = true;
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
print_colors();
|
||||
return STATUS_BUILTIN_OK;
|
||||
|
||||
case '?':
|
||||
return STATUS_BUILTIN_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
/* Remaining argument is foreground color */
|
||||
const wchar_t *fgcolor = NULL;
|
||||
if (woptind < argc)
|
||||
{
|
||||
if (woptind + 1 == argc)
|
||||
{
|
||||
fgcolor = argv[woptind];
|
||||
}
|
||||
else
|
||||
{
|
||||
append_format(stderr_buffer,
|
||||
_(L"%ls: Too many arguments\n"),
|
||||
argv[0]);
|
||||
return STATUS_BUILTIN_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (fgcolor == NULL && bgcolor == NULL && !bold && !underline)
|
||||
{
|
||||
append_format(stderr_buffer,
|
||||
_(L"%ls: Expected an argument\n"),
|
||||
argv[0]);
|
||||
return STATUS_BUILTIN_ERROR;
|
||||
}
|
||||
|
||||
const rgb_color_t fg = rgb_color_t(fgcolor ? fgcolor : L"");
|
||||
if (fgcolor && fg.is_none())
|
||||
{
|
||||
append_format(stderr_buffer, _(L"%ls: Unknown color '%ls'\n"), argv[0], fgcolor);
|
||||
return STATUS_BUILTIN_ERROR;
|
||||
}
|
||||
|
||||
const rgb_color_t bg = rgb_color_t(bgcolor ? bgcolor : L"");
|
||||
if (bgcolor && bg.is_none())
|
||||
{
|
||||
append_format(stderr_buffer, _(L"%ls: Unknown color '%ls'\n"), argv[0], bgcolor);
|
||||
return STATUS_BUILTIN_ERROR;
|
||||
}
|
||||
|
||||
/* Make sure that the term exists */
|
||||
if (cur_term == NULL && setupterm(0, STDOUT_FILENO, &errret) == ERR)
|
||||
{
|
||||
append_format(stderr_buffer, _(L"%ls: Could not set up terminal\n"), argv[0]);
|
||||
return STATUS_BUILTIN_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
Test if we have at least basic support for setting fonts, colors
|
||||
and related bits - otherwise just give up...
|
||||
*/
|
||||
if (! exit_attribute_mode)
|
||||
{
|
||||
return STATUS_BUILTIN_ERROR;
|
||||
}
|
||||
|
||||
|
||||
/* Save old output function so we can restore it */
|
||||
int (* const saved_writer_func)(char) = output_get_writer();
|
||||
|
||||
/* Set our output function, which writes to a std::string */
|
||||
builtin_set_color_output.clear();
|
||||
output_set_writer(set_color_builtin_outputter);
|
||||
|
||||
if (bold)
|
||||
{
|
||||
if (enter_bold_mode)
|
||||
writembs(tparm(enter_bold_mode));
|
||||
}
|
||||
|
||||
if (underline)
|
||||
{
|
||||
if (enter_underline_mode)
|
||||
writembs(enter_underline_mode);
|
||||
}
|
||||
|
||||
if (bgcolor != NULL)
|
||||
{
|
||||
if (bg.is_normal())
|
||||
{
|
||||
write_background_color(0);
|
||||
writembs(tparm(exit_attribute_mode));
|
||||
}
|
||||
}
|
||||
|
||||
if (fgcolor != NULL)
|
||||
{
|
||||
if (fg.is_normal())
|
||||
{
|
||||
write_foreground_color(0);
|
||||
writembs(tparm(exit_attribute_mode));
|
||||
}
|
||||
else
|
||||
{
|
||||
write_foreground_color(index_for_color(fg));
|
||||
}
|
||||
}
|
||||
|
||||
if (bgcolor != NULL)
|
||||
{
|
||||
if (! bg.is_normal())
|
||||
{
|
||||
write_background_color(index_for_color(bg));
|
||||
}
|
||||
}
|
||||
|
||||
/* Restore saved writer function */
|
||||
output_set_writer(saved_writer_func);
|
||||
|
||||
/* Output the collected string */
|
||||
std::string local_output;
|
||||
std::swap(builtin_set_color_output, local_output);
|
||||
stdout_buffer.append(str2wcstring(local_output));
|
||||
|
||||
return STATUS_BUILTIN_OK;
|
||||
}
|
||||
970
builtin_test.cpp
Normal file
970
builtin_test.cpp
Normal file
@@ -0,0 +1,970 @@
|
||||
/** \file builtin_test.cpp Functions defining the test builtin
|
||||
|
||||
Functions used for implementing the test builtin.
|
||||
Implemented from scratch (yes, really) by way of IEEE 1003.1 as reference.
|
||||
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "common.h"
|
||||
#include "builtin.h"
|
||||
#include "wutil.h"
|
||||
#include "proc.h"
|
||||
#include <sys/stat.h>
|
||||
#include <memory>
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
BUILTIN_TEST_SUCCESS = STATUS_BUILTIN_OK,
|
||||
BUILTIN_TEST_FAIL = STATUS_BUILTIN_ERROR
|
||||
};
|
||||
|
||||
|
||||
int builtin_test(parser_t &parser, wchar_t **argv);
|
||||
|
||||
static const wchar_t * const condstr[] =
|
||||
{
|
||||
L"!", L"&&", L"||", L"==", L"!=", L"<", L">", L"-nt", L"-ot", L"-ef", L"-eq",
|
||||
L"-ne", L"-lt", L"-gt", L"-le", L"-ge", L"=~"
|
||||
};
|
||||
|
||||
namespace test_expressions
|
||||
{
|
||||
|
||||
enum token_t
|
||||
{
|
||||
test_unknown, // arbitrary string
|
||||
|
||||
test_bang, // "!", inverts sense
|
||||
|
||||
test_filetype_b, // "-b", for block special files
|
||||
test_filetype_c, // "-c" for character special files
|
||||
test_filetype_d, // "-d" for directories
|
||||
test_filetype_e, // "-e" for files that exist
|
||||
test_filetype_f, // "-f" for for regular files
|
||||
test_filetype_g, // "-g" for set-group-id
|
||||
test_filetype_h, // "-h" for symbolic links
|
||||
test_filetype_L, // "-L", same as -h
|
||||
test_filetype_p, // "-p", for FIFO
|
||||
test_filetype_S, // "-S", socket
|
||||
|
||||
test_filesize_s, // "-s", size greater than zero
|
||||
|
||||
test_filedesc_t, // "-t", whether the fd is associated with a terminal
|
||||
|
||||
test_fileperm_r, // "-r", read permission
|
||||
test_fileperm_u, // "-u", whether file is setuid
|
||||
test_fileperm_w, // "-w", whether file write permission is allowed
|
||||
test_fileperm_x, // "-x", whether file execute/search is allowed
|
||||
|
||||
test_string_n, // "-n", non-empty string
|
||||
test_string_z, // "-z", true if length of string is 0
|
||||
test_string_equal, // "=", true if strings are identical
|
||||
test_string_not_equal, // "!=", true if strings are not identical
|
||||
|
||||
test_number_equal, // "-eq", true if numbers are equal
|
||||
test_number_not_equal, // "-ne", true if numbers are not equal
|
||||
test_number_greater, // "-gt", true if first number is larger than second
|
||||
test_number_greater_equal, // "-ge", true if first number is at least second
|
||||
test_number_lesser, // "-lt", true if first number is smaller than second
|
||||
test_number_lesser_equal, // "-le", true if first number is at most second
|
||||
|
||||
test_combine_and, // "-a", true if left and right are both true
|
||||
test_combine_or, // "-o", true if either left or right is true
|
||||
|
||||
test_paren_open, // "(", open paren
|
||||
test_paren_close, // ")", close paren
|
||||
};
|
||||
|
||||
static bool binary_primary_evaluate(test_expressions::token_t token, const wcstring &left, const wcstring &right, wcstring_list_t &errors);
|
||||
static bool unary_primary_evaluate(test_expressions::token_t token, const wcstring &arg, wcstring_list_t &errors);
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
UNARY_PRIMARY = 1 << 0,
|
||||
BINARY_PRIMARY = 1 << 1
|
||||
};
|
||||
|
||||
static const struct token_info_t
|
||||
{
|
||||
token_t tok;
|
||||
const wchar_t *string;
|
||||
unsigned int flags;
|
||||
} token_infos[] =
|
||||
{
|
||||
{test_unknown, L"", 0},
|
||||
{test_bang, L"!", 0},
|
||||
{test_filetype_b, L"-b", UNARY_PRIMARY},
|
||||
{test_filetype_c, L"-c", UNARY_PRIMARY},
|
||||
{test_filetype_d, L"-d", UNARY_PRIMARY},
|
||||
{test_filetype_e, L"-e", UNARY_PRIMARY},
|
||||
{test_filetype_f, L"-f", UNARY_PRIMARY},
|
||||
{test_filetype_g, L"-g", UNARY_PRIMARY},
|
||||
{test_filetype_h, L"-h", UNARY_PRIMARY},
|
||||
{test_filetype_L, L"-L", UNARY_PRIMARY},
|
||||
{test_filetype_p, L"-p", UNARY_PRIMARY},
|
||||
{test_filetype_S, L"-S", UNARY_PRIMARY},
|
||||
{test_filesize_s, L"-s", UNARY_PRIMARY},
|
||||
{test_filedesc_t, L"-t", UNARY_PRIMARY},
|
||||
{test_fileperm_r, L"-r", UNARY_PRIMARY},
|
||||
{test_fileperm_u, L"-u", UNARY_PRIMARY},
|
||||
{test_fileperm_w, L"-w", UNARY_PRIMARY},
|
||||
{test_fileperm_x, L"-x", UNARY_PRIMARY},
|
||||
{test_string_n, L"-n", UNARY_PRIMARY},
|
||||
{test_string_z, L"-z", UNARY_PRIMARY},
|
||||
{test_string_equal, L"=", BINARY_PRIMARY},
|
||||
{test_string_not_equal, L"!=", BINARY_PRIMARY},
|
||||
{test_number_equal, L"-eq", BINARY_PRIMARY},
|
||||
{test_number_not_equal, L"-ne", BINARY_PRIMARY},
|
||||
{test_number_greater, L"-gt", BINARY_PRIMARY},
|
||||
{test_number_greater_equal, L"-ge", BINARY_PRIMARY},
|
||||
{test_number_lesser, L"-lt", BINARY_PRIMARY},
|
||||
{test_number_lesser_equal, L"-le", BINARY_PRIMARY},
|
||||
{test_combine_and, L"-a", 0},
|
||||
{test_combine_or, L"-o", 0},
|
||||
{test_paren_open, L"(", 0},
|
||||
{test_paren_close, L")", 0}
|
||||
};
|
||||
|
||||
const token_info_t *token_for_string(const wcstring &str)
|
||||
{
|
||||
for (size_t i=0; i < sizeof token_infos / sizeof *token_infos; i++)
|
||||
{
|
||||
if (str == token_infos[i].string)
|
||||
{
|
||||
return &token_infos[i];
|
||||
}
|
||||
}
|
||||
return &token_infos[0]; //unknown
|
||||
}
|
||||
|
||||
|
||||
/* Grammar.
|
||||
|
||||
<expr> = <combining_expr>
|
||||
|
||||
<combining_expr> = <unary_expr> and/or <combining_expr> |
|
||||
<unary_expr>
|
||||
|
||||
<unary_expr> = bang <unary_expr> |
|
||||
<primary>
|
||||
|
||||
<primary> = <unary_primary> arg |
|
||||
arg <binary_primary> arg |
|
||||
'(' <expr> ')'
|
||||
|
||||
*/
|
||||
|
||||
class expression;
|
||||
class test_parser
|
||||
{
|
||||
private:
|
||||
wcstring_list_t strings;
|
||||
wcstring_list_t errors;
|
||||
|
||||
expression *error(const wchar_t *fmt, ...);
|
||||
void add_error(const wchar_t *fmt, ...);
|
||||
|
||||
const wcstring &arg(unsigned int idx)
|
||||
{
|
||||
return strings.at(idx);
|
||||
}
|
||||
|
||||
public:
|
||||
test_parser(const wcstring_list_t &val) : strings(val)
|
||||
{ }
|
||||
|
||||
expression *parse_expression(unsigned int start, unsigned int end);
|
||||
expression *parse_3_arg_expression(unsigned int start, unsigned int end);
|
||||
expression *parse_4_arg_expression(unsigned int start, unsigned int end);
|
||||
expression *parse_combining_expression(unsigned int start, unsigned int end);
|
||||
expression *parse_unary_expression(unsigned int start, unsigned int end);
|
||||
|
||||
expression *parse_primary(unsigned int start, unsigned int end);
|
||||
expression *parse_parenthentical(unsigned int start, unsigned int end);
|
||||
expression *parse_unary_primary(unsigned int start, unsigned int end);
|
||||
expression *parse_binary_primary(unsigned int start, unsigned int end);
|
||||
expression *parse_just_a_string(unsigned int start, unsigned int end);
|
||||
|
||||
static expression *parse_args(const wcstring_list_t &args, wcstring &err);
|
||||
};
|
||||
|
||||
struct range_t
|
||||
{
|
||||
unsigned int start;
|
||||
unsigned int end;
|
||||
|
||||
range_t(unsigned s, unsigned e) : start(s), end(e) { }
|
||||
};
|
||||
|
||||
|
||||
/* Base class for expressions */
|
||||
class expression
|
||||
{
|
||||
protected:
|
||||
expression(token_t what, range_t where) : token(what), range(where) { }
|
||||
|
||||
public:
|
||||
const token_t token;
|
||||
range_t range;
|
||||
|
||||
virtual ~expression() { }
|
||||
|
||||
// evaluate returns true if the expression is true (i.e. BUILTIN_TEST_SUCCESS)
|
||||
virtual bool evaluate(wcstring_list_t &errors) = 0;
|
||||
};
|
||||
|
||||
typedef std::auto_ptr<expression> expr_ref_t;
|
||||
|
||||
/* Single argument like -n foo or "just a string" */
|
||||
class unary_primary : public expression
|
||||
{
|
||||
public:
|
||||
wcstring arg;
|
||||
unary_primary(token_t tok, range_t where, const wcstring &what) : expression(tok, where), arg(what) { }
|
||||
bool evaluate(wcstring_list_t &errors);
|
||||
};
|
||||
|
||||
/* Two argument primary like foo != bar */
|
||||
class binary_primary : public expression
|
||||
{
|
||||
public:
|
||||
wcstring arg_left;
|
||||
wcstring arg_right;
|
||||
|
||||
binary_primary(token_t tok, range_t where, const wcstring &left, const wcstring &right) : expression(tok, where), arg_left(left), arg_right(right)
|
||||
{ }
|
||||
bool evaluate(wcstring_list_t &errors);
|
||||
};
|
||||
|
||||
/* Unary operator like bang */
|
||||
class unary_operator : public expression
|
||||
{
|
||||
public:
|
||||
expr_ref_t subject;
|
||||
unary_operator(token_t tok, range_t where, expr_ref_t &exp) : expression(tok, where), subject(exp) { }
|
||||
bool evaluate(wcstring_list_t &errors);
|
||||
};
|
||||
|
||||
/* Combining expression. Contains a list of AND or OR expressions. It takes more than two so that we don't have to worry about precedence in the parser. */
|
||||
class combining_expression : public expression
|
||||
{
|
||||
public:
|
||||
const std::vector<expression *> subjects;
|
||||
const std::vector<token_t> combiners;
|
||||
|
||||
combining_expression(token_t tok, range_t where, const std::vector<expression *> &exprs, const std::vector<token_t> &combs) : expression(tok, where), subjects(exprs), combiners(combs)
|
||||
{
|
||||
/* We should have one more subject than combiner */
|
||||
assert(subjects.size() == combiners.size() + 1);
|
||||
}
|
||||
|
||||
/* We are responsible for destroying our expressions */
|
||||
virtual ~combining_expression()
|
||||
{
|
||||
for (size_t i=0; i < subjects.size(); i++)
|
||||
{
|
||||
delete subjects[i];
|
||||
}
|
||||
}
|
||||
|
||||
bool evaluate(wcstring_list_t &errors);
|
||||
};
|
||||
|
||||
/* Parenthetical expression */
|
||||
class parenthetical_expression : public expression
|
||||
{
|
||||
public:
|
||||
expr_ref_t contents;
|
||||
parenthetical_expression(token_t tok, range_t where, expr_ref_t &expr) : expression(tok, where), contents(expr) { }
|
||||
|
||||
virtual bool evaluate(wcstring_list_t &errors);
|
||||
};
|
||||
|
||||
void test_parser::add_error(const wchar_t *fmt, ...)
|
||||
{
|
||||
assert(fmt != NULL);
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
this->errors.push_back(vformat_string(fmt, va));
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
expression *test_parser::error(const wchar_t *fmt, ...)
|
||||
{
|
||||
assert(fmt != NULL);
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
this->errors.push_back(vformat_string(fmt, va));
|
||||
va_end(va);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
expression *test_parser::parse_unary_expression(unsigned int start, unsigned int end)
|
||||
{
|
||||
if (start >= end)
|
||||
{
|
||||
return error(L"Missing argument at index %u", start);
|
||||
}
|
||||
token_t tok = token_for_string(arg(start))->tok;
|
||||
if (tok == test_bang)
|
||||
{
|
||||
expr_ref_t subject(parse_unary_expression(start + 1, end));
|
||||
if (subject.get())
|
||||
{
|
||||
return new unary_operator(tok, range_t(start, subject->range.end), subject);
|
||||
}
|
||||
else
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return parse_primary(start, end);
|
||||
}
|
||||
}
|
||||
|
||||
/* Parse a combining expression (AND, OR) */
|
||||
expression *test_parser::parse_combining_expression(unsigned int start, unsigned int end)
|
||||
{
|
||||
if (start >= end)
|
||||
return NULL;
|
||||
|
||||
std::vector<expression *> subjects;
|
||||
std::vector<token_t> combiners;
|
||||
unsigned int idx = start;
|
||||
bool first = true;
|
||||
|
||||
while (idx < end)
|
||||
{
|
||||
|
||||
if (! first)
|
||||
{
|
||||
/* This is not the first expression, so we expect a combiner. */
|
||||
token_t combiner = token_for_string(arg(idx))->tok;
|
||||
if (combiner != test_combine_and && combiner != test_combine_or)
|
||||
{
|
||||
/* Not a combiner, we're done */
|
||||
this->errors.insert(this->errors.begin(), format_string(L"Expected a combining operator like '-a' at index %u", idx));
|
||||
break;
|
||||
}
|
||||
combiners.push_back(combiner);
|
||||
idx++;
|
||||
}
|
||||
|
||||
/* Parse another expression */
|
||||
expression *expr = parse_unary_expression(idx, end);
|
||||
if (! expr)
|
||||
{
|
||||
add_error(L"Missing argument at index %u", idx);
|
||||
if (! first)
|
||||
{
|
||||
/* Clean up the dangling combiner, since it never got its right hand expression */
|
||||
combiners.pop_back();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* Go to the end of this expression */
|
||||
idx = expr->range.end;
|
||||
subjects.push_back(expr);
|
||||
first = false;
|
||||
}
|
||||
|
||||
if (! subjects.empty())
|
||||
{
|
||||
/* Our new expression takes ownership of all expressions we created. The token we pass is irrelevant. */
|
||||
return new combining_expression(test_combine_and, range_t(start, idx), subjects, combiners);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* No subjects */
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
expression *test_parser::parse_unary_primary(unsigned int start, unsigned int end)
|
||||
{
|
||||
/* We need two arguments */
|
||||
if (start >= end)
|
||||
{
|
||||
return error(L"Missing argument at index %u", start);
|
||||
}
|
||||
if (start + 1 >= end)
|
||||
{
|
||||
return error(L"Missing argument at index %u", start + 1);
|
||||
}
|
||||
|
||||
/* All our unary primaries are prefix, so the operator is at start. */
|
||||
const token_info_t *info = token_for_string(arg(start));
|
||||
if (!(info->flags & UNARY_PRIMARY))
|
||||
return NULL;
|
||||
|
||||
return new unary_primary(info->tok, range_t(start, start + 2), arg(start + 1));
|
||||
}
|
||||
|
||||
expression *test_parser::parse_just_a_string(unsigned int start, unsigned int end)
|
||||
{
|
||||
/* Handle a string as a unary primary that is not a token of any other type.
|
||||
e.g. 'test foo -a bar' should evaluate to true
|
||||
We handle this with a unary primary of test_string_n
|
||||
*/
|
||||
|
||||
/* We need one arguments */
|
||||
if (start >= end)
|
||||
{
|
||||
return error(L"Missing argument at index %u", start);
|
||||
}
|
||||
|
||||
const token_info_t *info = token_for_string(arg(start));
|
||||
if (info->tok != test_unknown)
|
||||
{
|
||||
return error(L"Unexpected argument type at index %u", start);
|
||||
}
|
||||
|
||||
/* This is hackish; a nicer way to implement this would be with a "just a string" expression type */
|
||||
return new unary_primary(test_string_n, range_t(start, start + 1), arg(start));
|
||||
}
|
||||
|
||||
#if 0
|
||||
expression *test_parser::parse_unary_primary(unsigned int start, unsigned int end)
|
||||
{
|
||||
/* We need either one or two arguments */
|
||||
if (start >= end)
|
||||
{
|
||||
return error(L"Missing argument at index %u", start);
|
||||
}
|
||||
|
||||
/* The index of the argument to the unary primary */
|
||||
unsigned int arg_idx;
|
||||
|
||||
/* All our unary primaries are prefix, so any operator is at start. But it also may just be a string, with no operator. */
|
||||
const token_info_t *info = token_for_string(arg(start));
|
||||
if (info->flags & UNARY_PRIMARY)
|
||||
{
|
||||
/* We have an operator. Skip the operator argument */
|
||||
arg_idx = start + 1;
|
||||
|
||||
/* We have some freedom here...do we allow other tokens for the argument to operate on?
|
||||
For example, should 'test -n =' work? I say yes. So no typechecking on the next token. */
|
||||
|
||||
}
|
||||
else if (info->tok == test_unknown)
|
||||
{
|
||||
/* "Just a string. */
|
||||
arg_idx = start;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Here we don't allow arbitrary tokens as "just a string." I.e. 'test = -a =' should have a parse error. We could relax this at some point. */
|
||||
return error(L"Parse error at argument index %u", start);
|
||||
}
|
||||
|
||||
/* Verify we have the argument we want, i.e. test -n should fail to parse */
|
||||
if (arg_idx >= end)
|
||||
{
|
||||
return error(L"Missing argument at index %u", arg_idx);
|
||||
}
|
||||
|
||||
return new unary_primary(info->tok, range_t(start, arg_idx + 1), arg(arg_idx));
|
||||
}
|
||||
#endif
|
||||
|
||||
expression *test_parser::parse_binary_primary(unsigned int start, unsigned int end)
|
||||
{
|
||||
/* We need three arguments */
|
||||
for (unsigned int idx = start; idx < start + 3; idx++)
|
||||
{
|
||||
if (idx >= end)
|
||||
{
|
||||
return error(L"Missing argument at index %u", idx);
|
||||
}
|
||||
}
|
||||
|
||||
/* All our binary primaries are infix, so the operator is at start + 1. */
|
||||
const token_info_t *info = token_for_string(arg(start + 1));
|
||||
if (!(info->flags & BINARY_PRIMARY))
|
||||
return NULL;
|
||||
|
||||
return new binary_primary(info->tok, range_t(start, start + 3), arg(start), arg(start + 2));
|
||||
}
|
||||
|
||||
expression *test_parser::parse_parenthentical(unsigned int start, unsigned int end)
|
||||
{
|
||||
/* We need at least three arguments: open paren, argument, close paren */
|
||||
if (start + 3 >= end)
|
||||
return NULL;
|
||||
|
||||
/* Must start with an open expression */
|
||||
const token_info_t *open_paren = token_for_string(arg(start));
|
||||
if (open_paren->tok != test_paren_open)
|
||||
return NULL;
|
||||
|
||||
/* Parse a subexpression */
|
||||
expression *subexr_ptr = parse_expression(start + 1, end);
|
||||
if (! subexr_ptr)
|
||||
return NULL;
|
||||
expr_ref_t subexpr(subexr_ptr);
|
||||
|
||||
/* Parse a close paren */
|
||||
unsigned close_index = subexpr->range.end;
|
||||
assert(close_index <= end);
|
||||
if (close_index == end)
|
||||
{
|
||||
return error(L"Missing close paren at index %u", close_index);
|
||||
}
|
||||
const token_info_t *close_paren = token_for_string(arg(close_index));
|
||||
if (close_paren->tok != test_paren_close)
|
||||
{
|
||||
return error(L"Expected close paren at index %u", close_index);
|
||||
}
|
||||
|
||||
/* Success */
|
||||
return new parenthetical_expression(test_paren_open, range_t(start, close_index+1), subexpr);
|
||||
}
|
||||
|
||||
expression *test_parser::parse_primary(unsigned int start, unsigned int end)
|
||||
{
|
||||
if (start >= end)
|
||||
{
|
||||
return error(L"Missing argument at index %u", start);
|
||||
}
|
||||
|
||||
expression *expr = NULL;
|
||||
if (! expr) expr = parse_parenthentical(start, end);
|
||||
if (! expr) expr = parse_unary_primary(start, end);
|
||||
if (! expr) expr = parse_binary_primary(start, end);
|
||||
if (! expr) expr = parse_just_a_string(start, end);
|
||||
return expr;
|
||||
}
|
||||
|
||||
// See IEEE 1003.1 breakdown of the behavior for different parameter counts
|
||||
expression *test_parser::parse_3_arg_expression(unsigned int start, unsigned int end)
|
||||
{
|
||||
assert(end - start == 3);
|
||||
expression *result = NULL;
|
||||
|
||||
const token_info_t *center_token = token_for_string(arg(start + 1));
|
||||
if (center_token->flags & BINARY_PRIMARY)
|
||||
{
|
||||
result = parse_binary_primary(start, end);
|
||||
}
|
||||
else if (center_token->tok == test_combine_and || center_token->tok == test_combine_or)
|
||||
{
|
||||
expr_ref_t left(parse_unary_expression(start, start + 1));
|
||||
expr_ref_t right(parse_unary_expression(start + 2, start + 3));
|
||||
if (left.get() && right.get())
|
||||
{
|
||||
// Transfer ownership to the vector of subjects
|
||||
std::vector<token_t> combiners(1, center_token->tok);
|
||||
std::vector<expression *> subjects;
|
||||
subjects.push_back(left.release());
|
||||
subjects.push_back(right.release());
|
||||
result = new combining_expression(center_token->tok, range_t(start, end), subjects, combiners);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result = parse_unary_expression(start, end);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
expression *test_parser::parse_4_arg_expression(unsigned int start, unsigned int end)
|
||||
{
|
||||
assert(end - start == 4);
|
||||
expression *result = NULL;
|
||||
|
||||
token_t first_token = token_for_string(arg(start))->tok;
|
||||
if (first_token == test_bang)
|
||||
{
|
||||
expr_ref_t subject(parse_3_arg_expression(start + 1, end));
|
||||
if (subject.get())
|
||||
{
|
||||
result = new unary_operator(first_token, range_t(start, subject->range.end), subject);
|
||||
}
|
||||
}
|
||||
else if (first_token == test_paren_open)
|
||||
{
|
||||
result = parse_parenthentical(start, end);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = parse_combining_expression(start, end);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
expression *test_parser::parse_expression(unsigned int start, unsigned int end)
|
||||
{
|
||||
if (start >= end)
|
||||
{
|
||||
return error(L"Missing argument at index %u", start);
|
||||
}
|
||||
|
||||
unsigned int argc = end - start;
|
||||
switch (argc)
|
||||
{
|
||||
case 0:
|
||||
assert(0); //should have been caught by the above test
|
||||
return NULL;
|
||||
|
||||
case 1:
|
||||
{
|
||||
return error(L"Missing argument at index %u", start + 1);
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
return parse_unary_expression(start, end);
|
||||
}
|
||||
|
||||
case 3:
|
||||
{
|
||||
return parse_3_arg_expression(start, end);
|
||||
}
|
||||
|
||||
case 4:
|
||||
{
|
||||
return parse_4_arg_expression(start, end);
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
return parse_combining_expression(start, end);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expression *test_parser::parse_args(const wcstring_list_t &args, wcstring &err)
|
||||
{
|
||||
/* Empty list and one-arg list should be handled by caller */
|
||||
assert(args.size() > 1);
|
||||
|
||||
test_parser parser(args);
|
||||
expression *result = parser.parse_expression(0, (unsigned int)args.size());
|
||||
|
||||
/* Handle errors */
|
||||
bool errored = false;
|
||||
for (size_t i = 0; i < parser.errors.size(); i++)
|
||||
{
|
||||
err.append(L"test: ");
|
||||
err.append(parser.errors.at(i));
|
||||
err.push_back(L'\n');
|
||||
errored = true;
|
||||
// For now we only show the first error
|
||||
break;
|
||||
}
|
||||
|
||||
if (result)
|
||||
{
|
||||
/* It's also an error if there are any unused arguments. This is not detected by parse_expression() */
|
||||
assert(result->range.end <= args.size());
|
||||
if (result->range.end < args.size())
|
||||
{
|
||||
if (err.empty())
|
||||
{
|
||||
append_format(err, L"test: unexpected argument at index %lu: '%ls'\n", (unsigned long)result->range.end, args.at(result->range.end).c_str());
|
||||
}
|
||||
errored = true;
|
||||
|
||||
delete result;
|
||||
result = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool unary_primary::evaluate(wcstring_list_t &errors)
|
||||
{
|
||||
return unary_primary_evaluate(token, arg, errors);
|
||||
}
|
||||
|
||||
bool binary_primary::evaluate(wcstring_list_t &errors)
|
||||
{
|
||||
return binary_primary_evaluate(token, arg_left, arg_right, errors);
|
||||
}
|
||||
|
||||
bool unary_operator::evaluate(wcstring_list_t &errors)
|
||||
{
|
||||
switch (token)
|
||||
{
|
||||
case test_bang:
|
||||
assert(subject.get());
|
||||
return ! subject->evaluate(errors);
|
||||
default:
|
||||
errors.push_back(format_string(L"Unknown token type in %s", __func__));
|
||||
return false;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
bool combining_expression::evaluate(wcstring_list_t &errors)
|
||||
{
|
||||
switch (token)
|
||||
{
|
||||
case test_combine_and:
|
||||
case test_combine_or:
|
||||
{
|
||||
/* One-element case */
|
||||
if (subjects.size() == 1)
|
||||
return subjects.at(0)->evaluate(errors);
|
||||
|
||||
/* Evaluate our lists, remembering that AND has higher precedence than OR. We can visualize this as a sequence of OR expressions of AND expressions. */
|
||||
assert(combiners.size() + 1 == subjects.size());
|
||||
assert(! subjects.empty());
|
||||
|
||||
size_t idx = 0, max = subjects.size();
|
||||
bool or_result = false;
|
||||
while (idx < max)
|
||||
{
|
||||
if (or_result)
|
||||
{
|
||||
/* Short circuit */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Evaluate a stream of AND starting at given subject index. It may only have one element. */
|
||||
bool and_result = true;
|
||||
for (; idx < max; idx++)
|
||||
{
|
||||
/* Evaluate it, short-circuiting */
|
||||
and_result = and_result && subjects.at(idx)->evaluate(errors);
|
||||
|
||||
/* If the combiner at this index (which corresponding to how we combine with the next subject) is not AND, then exit the loop */
|
||||
if (idx + 1 < max && combiners.at(idx) != test_combine_and)
|
||||
{
|
||||
idx++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* OR it in */
|
||||
or_result = or_result || and_result;
|
||||
}
|
||||
return or_result;
|
||||
}
|
||||
|
||||
default:
|
||||
errors.push_back(format_string(L"Unknown token type in %s", __func__));
|
||||
return BUILTIN_TEST_FAIL;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
bool parenthetical_expression::evaluate(wcstring_list_t &errors)
|
||||
{
|
||||
return contents->evaluate(errors);
|
||||
}
|
||||
|
||||
/* IEEE 1003.1 says nothing about what it means for two strings to be "algebraically equal". For example, should we interpret 0x10 as 0, 10, or 16? Here we use only base 10 and use wcstoll, which allows for leading + and -, and leading whitespace. This matches bash. */
|
||||
static bool parse_number(const wcstring &arg, long long *out)
|
||||
{
|
||||
const wchar_t *str = arg.c_str();
|
||||
wchar_t *endptr = NULL;
|
||||
*out = wcstoll(str, &endptr, 10);
|
||||
return endptr && *endptr == L'\0';
|
||||
}
|
||||
|
||||
static bool binary_primary_evaluate(test_expressions::token_t token, const wcstring &left, const wcstring &right, wcstring_list_t &errors)
|
||||
{
|
||||
using namespace test_expressions;
|
||||
long long left_num, right_num;
|
||||
switch (token)
|
||||
{
|
||||
case test_string_equal:
|
||||
return left == right;
|
||||
|
||||
case test_string_not_equal:
|
||||
return left != right;
|
||||
|
||||
case test_number_equal:
|
||||
return parse_number(left, &left_num) && parse_number(right, &right_num) && left_num == right_num;
|
||||
|
||||
case test_number_not_equal:
|
||||
return parse_number(left, &left_num) && parse_number(right, &right_num) && left_num != right_num;
|
||||
|
||||
case test_number_greater:
|
||||
return parse_number(left, &left_num) && parse_number(right, &right_num) && left_num > right_num;
|
||||
|
||||
case test_number_greater_equal:
|
||||
return parse_number(left, &left_num) && parse_number(right, &right_num) && left_num >= right_num;
|
||||
|
||||
case test_number_lesser:
|
||||
return parse_number(left, &left_num) && parse_number(right, &right_num) && left_num < right_num;
|
||||
|
||||
case test_number_lesser_equal:
|
||||
return parse_number(left, &left_num) && parse_number(right, &right_num) && left_num <= right_num;
|
||||
|
||||
default:
|
||||
errors.push_back(format_string(L"Unknown token type in %s", __func__));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static bool unary_primary_evaluate(test_expressions::token_t token, const wcstring &arg, wcstring_list_t &errors)
|
||||
{
|
||||
using namespace test_expressions;
|
||||
struct stat buf;
|
||||
long long num;
|
||||
switch (token)
|
||||
{
|
||||
case test_filetype_b: // "-b", for block special files
|
||||
return !wstat(arg, &buf) && S_ISBLK(buf.st_mode);
|
||||
|
||||
case test_filetype_c: // "-c" for character special files
|
||||
return !wstat(arg, &buf) && S_ISCHR(buf.st_mode);
|
||||
|
||||
case test_filetype_d: // "-d" for directories
|
||||
return !wstat(arg, &buf) && S_ISDIR(buf.st_mode);
|
||||
|
||||
case test_filetype_e: // "-e" for files that exist
|
||||
return !wstat(arg, &buf);
|
||||
|
||||
case test_filetype_f: // "-f" for for regular files
|
||||
return !wstat(arg, &buf) && S_ISREG(buf.st_mode);
|
||||
|
||||
case test_filetype_g: // "-g" for set-group-id
|
||||
return !wstat(arg, &buf) && (S_ISGID & buf.st_mode);
|
||||
|
||||
case test_filetype_h: // "-h" for symbolic links
|
||||
case test_filetype_L: // "-L", same as -h
|
||||
return !lwstat(arg, &buf) && S_ISLNK(buf.st_mode);
|
||||
|
||||
case test_filetype_p: // "-p", for FIFO
|
||||
return !wstat(arg, &buf) && S_ISFIFO(buf.st_mode);
|
||||
|
||||
case test_filetype_S: // "-S", socket
|
||||
return !wstat(arg, &buf) && S_ISSOCK(buf.st_mode);
|
||||
|
||||
case test_filesize_s: // "-s", size greater than zero
|
||||
return !wstat(arg, &buf) && buf.st_size > 0;
|
||||
|
||||
case test_filedesc_t: // "-t", whether the fd is associated with a terminal
|
||||
return parse_number(arg, &num) && num == (int)num && isatty((int)num);
|
||||
|
||||
case test_fileperm_r: // "-r", read permission
|
||||
return !waccess(arg, R_OK);
|
||||
|
||||
case test_fileperm_u: // "-u", whether file is setuid
|
||||
return !wstat(arg, &buf) && (S_ISUID & buf.st_mode);
|
||||
|
||||
case test_fileperm_w: // "-w", whether file write permission is allowed
|
||||
return !waccess(arg, W_OK);
|
||||
|
||||
case test_fileperm_x: // "-x", whether file execute/search is allowed
|
||||
return !waccess(arg, X_OK);
|
||||
|
||||
case test_string_n: // "-n", non-empty string
|
||||
return ! arg.empty();
|
||||
|
||||
case test_string_z: // "-z", true if length of string is 0
|
||||
return arg.empty();
|
||||
|
||||
default:
|
||||
errors.push_back(format_string(L"Unknown token type in %s", __func__));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
* Evaluate a conditional expression given the arguments.
|
||||
* If fromtest is set, the caller is the test or [ builtin;
|
||||
* with the pointer giving the name of the command.
|
||||
* for POSIX conformance this supports a more limited range
|
||||
* of functionality.
|
||||
*
|
||||
* Return status is the final shell status, i.e. 0 for true,
|
||||
* 1 for false and 2 for error.
|
||||
*/
|
||||
int builtin_test(parser_t &parser, wchar_t **argv)
|
||||
{
|
||||
using namespace test_expressions;
|
||||
|
||||
/* The first argument should be the name of the command ('test') */
|
||||
if (! argv[0])
|
||||
return BUILTIN_TEST_FAIL;
|
||||
|
||||
/* Whether we are invoked with bracket '[' or not */
|
||||
const bool is_bracket = ! wcscmp(argv[0], L"[");
|
||||
|
||||
size_t argc = 0;
|
||||
while (argv[argc + 1])
|
||||
argc++;
|
||||
|
||||
/* If we're bracket, the last argument ought to be ]; we ignore it. Note that argc is the number of arguments after the command name; thus argv[argc] is the last argument. */
|
||||
if (is_bracket)
|
||||
{
|
||||
if (! wcscmp(argv[argc], L"]"))
|
||||
{
|
||||
/* Ignore the closing bracketp */
|
||||
argc--;
|
||||
}
|
||||
else
|
||||
{
|
||||
builtin_show_error(L"[: the last argument must be ']'\n");
|
||||
return BUILTIN_TEST_FAIL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Collect the arguments into a list */
|
||||
const wcstring_list_t args(argv + 1, argv + 1 + argc);
|
||||
|
||||
switch (argc)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
// Per 1003.1, exit false
|
||||
return BUILTIN_TEST_FAIL;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
// Per 1003.1, exit true if the arg is non-empty
|
||||
return args.at(0).empty() ? BUILTIN_TEST_FAIL : BUILTIN_TEST_SUCCESS;
|
||||
}
|
||||
default:
|
||||
{
|
||||
// Try parsing. If expr is not nil, we are responsible for deleting it.
|
||||
wcstring err;
|
||||
expression *expr = test_parser::parse_args(args, err);
|
||||
if (! expr)
|
||||
{
|
||||
#if 0
|
||||
printf("Oops! test was given args:\n");
|
||||
for (size_t i=0; i < argc; i++)
|
||||
{
|
||||
printf("\t%ls\n", args.at(i).c_str());
|
||||
}
|
||||
printf("and returned parse error: %ls\n", err.c_str());
|
||||
#endif
|
||||
builtin_show_error(err);
|
||||
return BUILTIN_TEST_FAIL;
|
||||
}
|
||||
else
|
||||
{
|
||||
wcstring_list_t eval_errors;
|
||||
bool result = expr->evaluate(eval_errors);
|
||||
if (! eval_errors.empty())
|
||||
{
|
||||
printf("test returned eval errors:\n");
|
||||
for (size_t i=0; i < eval_errors.size(); i++)
|
||||
{
|
||||
printf("\t%ls\n", eval_errors.at(i).c_str());
|
||||
}
|
||||
}
|
||||
delete expr;
|
||||
return result ? BUILTIN_TEST_SUCCESS : BUILTIN_TEST_FAIL;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
512
builtin_ulimit.cpp
Normal file
512
builtin_ulimit.cpp
Normal file
@@ -0,0 +1,512 @@
|
||||
/** \file builtin_ulimit.c Functions defining the ulimit builtin
|
||||
|
||||
Functions used for implementing the ulimit builtin.
|
||||
|
||||
*/
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <wchar.h>
|
||||
#include <wctype.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/resource.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "fallback.h"
|
||||
#include "util.h"
|
||||
|
||||
#include "builtin.h"
|
||||
#include "common.h"
|
||||
#include "wgetopt.h"
|
||||
|
||||
|
||||
/**
|
||||
Struct describing a resource limit
|
||||
*/
|
||||
struct resource_t
|
||||
{
|
||||
/**
|
||||
Resource id
|
||||
*/
|
||||
int resource;
|
||||
/**
|
||||
Description of resource
|
||||
*/
|
||||
const wchar_t *desc;
|
||||
/**
|
||||
Switch used on commandline to specify resource
|
||||
*/
|
||||
wchar_t switch_char;
|
||||
/**
|
||||
The implicit multiplier used when setting getting values
|
||||
*/
|
||||
int multiplier;
|
||||
}
|
||||
;
|
||||
|
||||
/**
|
||||
Array of resource_t structs, describing all known resource types.
|
||||
*/
|
||||
static const struct resource_t resource_arr[] =
|
||||
{
|
||||
{
|
||||
RLIMIT_CORE, L"Maximum size of core files created", L'c', 1024
|
||||
}
|
||||
,
|
||||
{
|
||||
RLIMIT_DATA, L"Maximum size of a process’s data segment", L'd', 1024
|
||||
}
|
||||
,
|
||||
{
|
||||
RLIMIT_FSIZE, L"Maximum size of files created by the shell", L'f', 1024
|
||||
}
|
||||
,
|
||||
#ifdef RLIMIT_MEMLOCK
|
||||
{
|
||||
RLIMIT_MEMLOCK, L"Maximum size that may be locked into memory", L'l', 1024
|
||||
}
|
||||
,
|
||||
#endif
|
||||
#ifdef RLIMIT_RSS
|
||||
{
|
||||
RLIMIT_RSS, L"Maximum resident set size", L'm', 1024
|
||||
}
|
||||
,
|
||||
#endif
|
||||
{
|
||||
RLIMIT_NOFILE, L"Maximum number of open file descriptors", L'n', 1
|
||||
}
|
||||
,
|
||||
{
|
||||
RLIMIT_STACK, L"Maximum stack size", L's', 1024
|
||||
}
|
||||
,
|
||||
{
|
||||
RLIMIT_CPU, L"Maximum amount of cpu time in seconds", L't', 1
|
||||
}
|
||||
,
|
||||
#ifdef RLIMIT_NPROC
|
||||
{
|
||||
RLIMIT_NPROC, L"Maximum number of processes available to a single user", L'u', 1
|
||||
}
|
||||
,
|
||||
#endif
|
||||
#ifdef RLIMIT_AS
|
||||
{
|
||||
RLIMIT_AS, L"Maximum amount of virtual memory available to the shell", L'v', 1024
|
||||
}
|
||||
,
|
||||
#endif
|
||||
{
|
||||
0, 0, 0, 0
|
||||
}
|
||||
}
|
||||
;
|
||||
|
||||
/**
|
||||
Get the implicit multiplication factor for the specified resource limit
|
||||
*/
|
||||
static int get_multiplier(int what)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; resource_arr[i].desc; i++)
|
||||
{
|
||||
if (resource_arr[i].resource == what)
|
||||
{
|
||||
return resource_arr[i].multiplier;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
Return the value for the specified resource limit. This function
|
||||
does _not_ multiply the limit value by the multiplier constant used
|
||||
by the commandline ulimit.
|
||||
*/
|
||||
static rlim_t get(int resource, int hard)
|
||||
{
|
||||
struct rlimit ls;
|
||||
|
||||
getrlimit(resource, &ls);
|
||||
|
||||
return hard ? ls.rlim_max:ls.rlim_cur;
|
||||
}
|
||||
|
||||
/**
|
||||
Print the value of the specified resource limit
|
||||
*/
|
||||
static void print(int resource, int hard)
|
||||
{
|
||||
rlim_t l = get(resource, hard);
|
||||
|
||||
if (l == RLIM_INFINITY)
|
||||
stdout_buffer.append(L"unlimited\n");
|
||||
else
|
||||
append_format(stdout_buffer, L"%d\n", l / get_multiplier(resource));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
Print values of all resource limits
|
||||
*/
|
||||
static void print_all(int hard)
|
||||
{
|
||||
int i;
|
||||
int w=0;
|
||||
|
||||
for (i=0; resource_arr[i].desc; i++)
|
||||
{
|
||||
w=maxi(w, my_wcswidth(resource_arr[i].desc));
|
||||
}
|
||||
|
||||
for (i=0; resource_arr[i].desc; i++)
|
||||
{
|
||||
struct rlimit ls;
|
||||
rlim_t l;
|
||||
getrlimit(resource_arr[i].resource, &ls);
|
||||
l = hard ? ls.rlim_max:ls.rlim_cur;
|
||||
|
||||
const wchar_t *unit = ((resource_arr[i].resource==RLIMIT_CPU)?L"(seconds, ":(get_multiplier(resource_arr[i].resource)==1?L"(":L"(kB, "));
|
||||
|
||||
append_format(stdout_buffer,
|
||||
L"%-*ls %10ls-%lc) ",
|
||||
w,
|
||||
resource_arr[i].desc,
|
||||
unit,
|
||||
resource_arr[i].switch_char);
|
||||
|
||||
if (l == RLIM_INFINITY)
|
||||
{
|
||||
stdout_buffer.append(L"unlimited\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
append_format(stdout_buffer, L"%d\n", l/get_multiplier(resource_arr[i].resource));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the description for the specified resource limit
|
||||
*/
|
||||
static const wchar_t *get_desc(int what)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; resource_arr[i].desc; i++)
|
||||
{
|
||||
if (resource_arr[i].resource == what)
|
||||
{
|
||||
return resource_arr[i].desc;
|
||||
}
|
||||
}
|
||||
return L"Not a resource";
|
||||
}
|
||||
|
||||
/**
|
||||
Set the new value of the specified resource limit. This function
|
||||
does _not_ multiply the limit value by the multiplier constant used
|
||||
by the commandline ulimit.
|
||||
*/
|
||||
static int set(int resource, int hard, int soft, rlim_t value)
|
||||
{
|
||||
struct rlimit ls;
|
||||
getrlimit(resource, &ls);
|
||||
|
||||
if (hard)
|
||||
{
|
||||
ls.rlim_max = value;
|
||||
}
|
||||
|
||||
if (soft)
|
||||
{
|
||||
ls.rlim_cur = value;
|
||||
|
||||
/*
|
||||
Do not attempt to set the soft limit higher than the hard limit
|
||||
*/
|
||||
if ((value == RLIM_INFINITY && ls.rlim_max != RLIM_INFINITY) ||
|
||||
(value != RLIM_INFINITY && ls.rlim_max != RLIM_INFINITY && value > ls.rlim_max))
|
||||
{
|
||||
ls.rlim_cur = ls.rlim_max;
|
||||
}
|
||||
}
|
||||
|
||||
if (setrlimit(resource, &ls))
|
||||
{
|
||||
if (errno == EPERM)
|
||||
append_format(stderr_buffer, L"ulimit: Permission denied when changing resource of type '%ls'\n", get_desc(resource));
|
||||
else
|
||||
builtin_wperror(L"ulimit");
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
The ulimit builtin, used for setting resource limits. Defined in
|
||||
builtin_ulimit.c.
|
||||
*/
|
||||
static int builtin_ulimit(parser_t &parser, wchar_t ** argv)
|
||||
{
|
||||
int hard=0;
|
||||
int soft=0;
|
||||
|
||||
int what = RLIMIT_FSIZE;
|
||||
int report_all = 0;
|
||||
|
||||
int argc = builtin_count_args(argv);
|
||||
|
||||
woptind=0;
|
||||
|
||||
while (1)
|
||||
{
|
||||
static const struct woption
|
||||
long_options[] =
|
||||
{
|
||||
{
|
||||
L"all", no_argument, 0, 'a'
|
||||
}
|
||||
,
|
||||
{
|
||||
L"hard", no_argument, 0, 'H'
|
||||
}
|
||||
,
|
||||
{
|
||||
L"soft", no_argument, 0, 'S'
|
||||
}
|
||||
,
|
||||
{
|
||||
L"core-size", no_argument, 0, 'c'
|
||||
}
|
||||
,
|
||||
{
|
||||
L"data-size", no_argument, 0, 'd'
|
||||
}
|
||||
,
|
||||
{
|
||||
L"file-size", no_argument, 0, 'f'
|
||||
}
|
||||
,
|
||||
{
|
||||
L"lock-size", no_argument, 0, 'l'
|
||||
}
|
||||
,
|
||||
{
|
||||
L"resident-set-size", no_argument, 0, 'm'
|
||||
}
|
||||
,
|
||||
{
|
||||
L"file-descriptor-count", no_argument, 0, 'n'
|
||||
}
|
||||
,
|
||||
{
|
||||
L"stack-size", no_argument, 0, 's'
|
||||
}
|
||||
,
|
||||
{
|
||||
L"cpu-time", no_argument, 0, 't'
|
||||
}
|
||||
,
|
||||
{
|
||||
L"process-count", no_argument, 0, 'u'
|
||||
}
|
||||
,
|
||||
{
|
||||
L"virtual-memory-size", no_argument, 0, 'v'
|
||||
}
|
||||
,
|
||||
{
|
||||
L"help", no_argument, 0, 'h'
|
||||
}
|
||||
,
|
||||
{
|
||||
0, 0, 0, 0
|
||||
}
|
||||
}
|
||||
;
|
||||
|
||||
|
||||
int opt_index = 0;
|
||||
|
||||
int opt = wgetopt_long(argc,
|
||||
argv,
|
||||
L"aHScdflmnstuvh",
|
||||
long_options,
|
||||
&opt_index);
|
||||
if (opt == -1)
|
||||
break;
|
||||
|
||||
switch (opt)
|
||||
{
|
||||
case 0:
|
||||
if (long_options[opt_index].flag != 0)
|
||||
break;
|
||||
append_format(stderr_buffer,
|
||||
BUILTIN_ERR_UNKNOWN,
|
||||
argv[0],
|
||||
long_options[opt_index].name);
|
||||
builtin_print_help(parser, argv[0], stderr_buffer);
|
||||
|
||||
return 1;
|
||||
|
||||
case L'a':
|
||||
report_all=1;
|
||||
break;
|
||||
|
||||
case L'H':
|
||||
hard=1;
|
||||
break;
|
||||
|
||||
case L'S':
|
||||
soft=1;
|
||||
break;
|
||||
|
||||
case L'c':
|
||||
what=RLIMIT_CORE;
|
||||
break;
|
||||
|
||||
case L'd':
|
||||
what=RLIMIT_DATA;
|
||||
break;
|
||||
|
||||
case L'f':
|
||||
what=RLIMIT_FSIZE;
|
||||
break;
|
||||
#ifdef RLIMIT_MEMLOCK
|
||||
case L'l':
|
||||
what=RLIMIT_MEMLOCK;
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifdef RLIMIT_RSS
|
||||
case L'm':
|
||||
what=RLIMIT_RSS;
|
||||
break;
|
||||
#endif
|
||||
|
||||
case L'n':
|
||||
what=RLIMIT_NOFILE;
|
||||
break;
|
||||
|
||||
case L's':
|
||||
what=RLIMIT_STACK;
|
||||
break;
|
||||
|
||||
case L't':
|
||||
what=RLIMIT_CPU;
|
||||
break;
|
||||
|
||||
#ifdef RLIMIT_NPROC
|
||||
case L'u':
|
||||
what=RLIMIT_NPROC;
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifdef RLIMIT_AS
|
||||
case L'v':
|
||||
what=RLIMIT_AS;
|
||||
break;
|
||||
#endif
|
||||
|
||||
case L'h':
|
||||
builtin_print_help(parser, argv[0], stdout_buffer);
|
||||
return 0;
|
||||
|
||||
case L'?':
|
||||
builtin_unknown_option(parser, argv[0], argv[woptind-1]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (report_all)
|
||||
{
|
||||
if (argc - woptind == 0)
|
||||
{
|
||||
print_all(hard);
|
||||
}
|
||||
else
|
||||
{
|
||||
stderr_buffer.append(argv[0]);
|
||||
stderr_buffer.append(L": Too many arguments\n");
|
||||
builtin_print_help(parser, argv[0], stderr_buffer);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (argc - woptind)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
/*
|
||||
Show current limit value
|
||||
*/
|
||||
print(what, hard);
|
||||
break;
|
||||
}
|
||||
|
||||
case 1:
|
||||
{
|
||||
/*
|
||||
Change current limit value
|
||||
*/
|
||||
rlim_t new_limit;
|
||||
wchar_t *end;
|
||||
|
||||
/*
|
||||
Set both hard and soft limits if nothing else was specified
|
||||
*/
|
||||
if (!(hard+soft))
|
||||
{
|
||||
hard=soft=1;
|
||||
}
|
||||
|
||||
if (wcscasecmp(argv[woptind], L"unlimited")==0)
|
||||
{
|
||||
new_limit = RLIM_INFINITY;
|
||||
}
|
||||
else if (wcscasecmp(argv[woptind], L"hard")==0)
|
||||
{
|
||||
new_limit = get(what, 1);
|
||||
}
|
||||
else if (wcscasecmp(argv[woptind], L"soft")==0)
|
||||
{
|
||||
new_limit = get(what, soft);
|
||||
}
|
||||
else
|
||||
{
|
||||
errno=0;
|
||||
new_limit = wcstol(argv[woptind], &end, 10);
|
||||
if (errno || *end)
|
||||
{
|
||||
append_format(stderr_buffer,
|
||||
L"%ls: Invalid limit '%ls'\n",
|
||||
argv[0],
|
||||
argv[woptind]);
|
||||
builtin_print_help(parser, argv[0], stderr_buffer);
|
||||
return 1;
|
||||
}
|
||||
new_limit *= get_multiplier(what);
|
||||
}
|
||||
|
||||
return set(what, hard, soft, new_limit);
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
stderr_buffer.append(argv[0]);
|
||||
stderr_buffer.append(L": Too many arguments\n");
|
||||
builtin_print_help(parser, argv[0], stderr_buffer);
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user