mirror of
https://github.com/uutils/sed.git
synced 2026-06-10 16:14:15 -07:00
Compare commits
70
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d23df4c400 | ||
|
|
8039955354 | ||
|
|
8a2934e519 | ||
|
|
a0d82d678d | ||
|
|
31de5d5ea0 | ||
|
|
78d3b3acf7 | ||
|
|
d9a1cdb9a2 | ||
|
|
1ccd8d069a | ||
|
|
4fb1046c54 | ||
|
|
b0e7940484 | ||
|
|
89b7a039c9 | ||
|
|
398ee76b79 | ||
|
|
ea5a3e45df | ||
|
|
c5e6348825 | ||
|
|
fc39ff7329 | ||
|
|
f6d26ff00f | ||
|
|
c6479b44ad | ||
|
|
8e3d3985e1 | ||
|
|
1710d9cc56 | ||
|
|
b26f59ab2f | ||
|
|
5d452d4174 | ||
|
|
ff0c1cdd06 | ||
|
|
4e024a9576 | ||
|
|
d89243bdb9 | ||
|
|
270f3fdce8 | ||
|
|
69ddd044c5 | ||
|
|
5ff4a7c0c2 | ||
|
|
ce3e4b33b5 | ||
|
|
35ea457759 | ||
|
|
35b6e84c16 | ||
|
|
faea122f87 | ||
|
|
db58849440 | ||
|
|
94bd9b4f14 | ||
|
|
d0ad8eb95d | ||
|
|
df06ef663d | ||
|
|
e82023054e | ||
|
|
9b7b509268 | ||
|
|
70ce8fe410 | ||
|
|
e16a49bb93 | ||
|
|
a8887f83dd | ||
|
|
9667ebe8c3 | ||
|
|
5ed30bd344 | ||
|
|
1996ac2ae3 | ||
|
|
e40ddb1c99 | ||
|
|
8c840078c5 | ||
|
|
4029ae5aba | ||
|
|
3479e9b580 | ||
|
|
0109b369a6 | ||
|
|
77e1abd18e | ||
|
|
17575017d8 | ||
|
|
902611caf1 | ||
|
|
b5a8444f88 | ||
|
|
200bc978b4 | ||
|
|
d58547195b | ||
|
|
ef7f8ad361 | ||
|
|
19f02d5683 | ||
|
|
d253a5808a | ||
|
|
bf47d12e37 | ||
|
|
0ea19a6fd5 | ||
|
|
55177aeca4 | ||
|
|
c8b9173752 | ||
|
|
3fa2227ebf | ||
|
|
2df1e00d8e | ||
|
|
61c405c549 | ||
|
|
3336f93514 | ||
|
|
569f3557e5 | ||
|
|
9ae154a26d | ||
|
|
2ae5b782c7 | ||
|
|
eddee66b6c | ||
|
|
b3ee5bfa9d |
@@ -0,0 +1,80 @@
|
|||||||
|
name: GnuComment
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_run:
|
||||||
|
workflows: ["GnuTests"]
|
||||||
|
types:
|
||||||
|
- completed
|
||||||
|
|
||||||
|
permissions: {}
|
||||||
|
jobs:
|
||||||
|
post-comment:
|
||||||
|
permissions:
|
||||||
|
actions: read # to list workflow runs artifacts
|
||||||
|
pull-requests: write # to comment on pr
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: >
|
||||||
|
github.event.workflow_run.event == 'pull_request'
|
||||||
|
steps:
|
||||||
|
- name: 'Download artifact'
|
||||||
|
uses: actions/github-script@v8
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
// List all artifacts from GnuTests
|
||||||
|
var artifacts = await github.rest.actions.listWorkflowRunArtifacts({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
run_id: ${{ github.event.workflow_run.id }},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Download the "comment" artifact, which contains a PR number (NR) and result.txt
|
||||||
|
var matchArtifact = artifacts.data.artifacts.filter((artifact) => {
|
||||||
|
return artifact.name == "comment"
|
||||||
|
})[0];
|
||||||
|
|
||||||
|
if (!matchArtifact) {
|
||||||
|
console.log('No comment artifact found');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var download = await github.rest.actions.downloadArtifact({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
artifact_id: matchArtifact.id,
|
||||||
|
archive_format: 'zip',
|
||||||
|
});
|
||||||
|
var fs = require('fs');
|
||||||
|
fs.writeFileSync('${{ github.workspace }}/comment.zip', Buffer.from(download.data));
|
||||||
|
- run: unzip comment.zip || echo "Failed to unzip comment artifact"
|
||||||
|
|
||||||
|
- name: 'Comment on PR'
|
||||||
|
uses: actions/github-script@v8
|
||||||
|
with:
|
||||||
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
script: |
|
||||||
|
var fs = require('fs');
|
||||||
|
|
||||||
|
// Check if files exist
|
||||||
|
if (!fs.existsSync('./NR')) {
|
||||||
|
console.log('No NR file found, skipping comment');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!fs.existsSync('./result.txt')) {
|
||||||
|
console.log('No result.txt file found, skipping comment');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var issue_number = Number(fs.readFileSync('./NR'));
|
||||||
|
var content = fs.readFileSync('./result.txt');
|
||||||
|
|
||||||
|
if (content.toString().trim().length > 7) { // 7 because we have backquote + \n
|
||||||
|
await github.rest.issues.createComment({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
issue_number: issue_number,
|
||||||
|
body: 'GNU sed testsuite comparison:\n```\n' + content + '```'
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log('Comment content too short, skipping');
|
||||||
|
}
|
||||||
@@ -0,0 +1,284 @@
|
|||||||
|
name: GnuTests
|
||||||
|
|
||||||
|
# Run GNU sed testsuite against the Rust sed implementation
|
||||||
|
# This workflow extracts and runs tests from the GNU sed testsuite to ensure compatibility
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- '*'
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write # Publish sed instead of discarding
|
||||||
|
|
||||||
|
# End the current execution if there is a new changeset in the PR
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
|
||||||
|
|
||||||
|
env:
|
||||||
|
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
|
||||||
|
TEST_FULL_SUMMARY_FILE: 'sed-gnu-full-result.json'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
native:
|
||||||
|
name: Run GNU sed testsuite
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
steps:
|
||||||
|
#### Get the code, setup cache
|
||||||
|
- name: Checkout code (sed)
|
||||||
|
uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
path: 'sed'
|
||||||
|
persist-credentials: false
|
||||||
|
- uses: dtolnay/rust-toolchain@master
|
||||||
|
with:
|
||||||
|
toolchain: stable
|
||||||
|
components: rustfmt
|
||||||
|
- uses: Swatinem/rust-cache@v2
|
||||||
|
with:
|
||||||
|
workspaces: "./sed -> target"
|
||||||
|
- name: Checkout code (GNU sed testsuite)
|
||||||
|
uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
repository: 'mirror/sed'
|
||||||
|
path: 'gnu.sed'
|
||||||
|
ref: 'master'
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
# Omit installing part of https://github.com/actions/runner-images/tree/main/images/ubuntu
|
||||||
|
|
||||||
|
### Build
|
||||||
|
- name: Build Rust sed binary
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
## Build Rust sed binary
|
||||||
|
cd 'sed'
|
||||||
|
cargo build --config=profile.release.strip=true --profile=release #-fast
|
||||||
|
zstd -19 target/release/sed -o ../sed-x86_64-unknown-linux-gnu.zst
|
||||||
|
- name: Publish latest commit
|
||||||
|
uses: softprops/action-gh-release@v2
|
||||||
|
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
||||||
|
with:
|
||||||
|
tag_name: latest-commit
|
||||||
|
draft: false
|
||||||
|
prerelease: true
|
||||||
|
files: |
|
||||||
|
sed-x86_64-unknown-linux-gnu.zst
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
### Run tests
|
||||||
|
- name: Run GNU sed testsuite
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
## Run GNU sed testsuite using our script
|
||||||
|
cd 'sed'
|
||||||
|
# Set GNU testsuite directory
|
||||||
|
export GNU_TESTSUITE_DIR="../gnu.sed/testsuite"
|
||||||
|
# Run tests with JSON output
|
||||||
|
./util/run-gnu-testsuite.sh --json-output "${{ env.TEST_FULL_SUMMARY_FILE }}" || true
|
||||||
|
|
||||||
|
### Upload artifacts
|
||||||
|
- name: Check for JSON results file
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
echo "Checking for JSON results file..."
|
||||||
|
ls -la sed/ || true
|
||||||
|
ls -la sed/${{ env.TEST_FULL_SUMMARY_FILE }} || echo "JSON file not found at sed/${{ env.TEST_FULL_SUMMARY_FILE }}"
|
||||||
|
ls -la ${{ env.TEST_FULL_SUMMARY_FILE }} || echo "JSON file not found at ${{ env.TEST_FULL_SUMMARY_FILE }}"
|
||||||
|
find . -name "*json*" -type f || echo "No JSON files found"
|
||||||
|
|
||||||
|
- name: Upload full json results
|
||||||
|
uses: actions/upload-artifact@v6
|
||||||
|
with:
|
||||||
|
name: sed-gnu-full-result
|
||||||
|
path: sed/${{ env.TEST_FULL_SUMMARY_FILE }}
|
||||||
|
if-no-files-found: warn
|
||||||
|
|
||||||
|
- name: Upload test logs
|
||||||
|
if: always()
|
||||||
|
uses: actions/upload-artifact@v6
|
||||||
|
with:
|
||||||
|
name: test-logs
|
||||||
|
path: |
|
||||||
|
sed/test-logs/*.log
|
||||||
|
sed/test-results/*.json
|
||||||
|
|
||||||
|
aggregate:
|
||||||
|
needs: [native]
|
||||||
|
permissions:
|
||||||
|
actions: read
|
||||||
|
contents: read
|
||||||
|
pull-requests: read
|
||||||
|
name: Aggregate GNU test results
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
steps:
|
||||||
|
- name: Initialize workflow variables
|
||||||
|
id: vars
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
## VARs setup
|
||||||
|
outputs() { step_id="${{ github.action }}"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo "${var}=${!var}" >> $GITHUB_OUTPUT; done; }
|
||||||
|
|
||||||
|
TEST_SUMMARY_FILE='sed-gnu-result.json'
|
||||||
|
outputs TEST_SUMMARY_FILE
|
||||||
|
|
||||||
|
- name: Checkout code (sed)
|
||||||
|
uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
path: 'sed'
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- name: Retrieve reference artifacts
|
||||||
|
uses: dawidd6/action-download-artifact@v12
|
||||||
|
continue-on-error: true
|
||||||
|
with:
|
||||||
|
workflow: GnuTests.yml
|
||||||
|
branch: "${{ env.DEFAULT_BRANCH }}"
|
||||||
|
workflow_conclusion: completed
|
||||||
|
path: "reference"
|
||||||
|
if_no_artifact_found: warn
|
||||||
|
|
||||||
|
- name: Download full json results
|
||||||
|
uses: actions/download-artifact@v7
|
||||||
|
with:
|
||||||
|
name: sed-gnu-full-result
|
||||||
|
path: results
|
||||||
|
|
||||||
|
- name: Extract/summarize testing info
|
||||||
|
id: summary
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
## Extract/summarize testing info
|
||||||
|
outputs() { step_id="${{ github.action }}"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo "${var}=${!var}" >> $GITHUB_OUTPUT; done; }
|
||||||
|
|
||||||
|
# Check if results directory exists and has JSON files
|
||||||
|
json_count=0
|
||||||
|
if [[ -d "results" ]]; then
|
||||||
|
json_count=$(find results -name "*.json" | wc -l)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$json_count" -lt 1 ]]; then
|
||||||
|
echo "::error ::Failed to download results json files; failing early"
|
||||||
|
echo "::error ::Contents of results directory:"
|
||||||
|
ls -lR results || echo "::error ::Results directory does not exist"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Extract summary from JSON results
|
||||||
|
RESULT_FILE="results/${{ env.TEST_FULL_SUMMARY_FILE }}"
|
||||||
|
if [[ -f "$RESULT_FILE" ]]; then
|
||||||
|
TOTAL=$(jq -r '.summary.total // 0' "$RESULT_FILE")
|
||||||
|
PASS=$(jq -r '.summary.passed // 0' "$RESULT_FILE")
|
||||||
|
FAIL=$(jq -r '.summary.failed // 0' "$RESULT_FILE")
|
||||||
|
SKIP=$(jq -r '.summary.skipped // 0' "$RESULT_FILE")
|
||||||
|
ERROR=0 # Our format doesn't distinguish errors from failures
|
||||||
|
else
|
||||||
|
echo "::error ::Result file $RESULT_FILE not found"
|
||||||
|
echo "::error ::Available files in results:"
|
||||||
|
find results -type f || true
|
||||||
|
TOTAL=0; PASS=0; FAIL=0; SKIP=0; ERROR=0
|
||||||
|
fi
|
||||||
|
|
||||||
|
output="GNU sed tests summary = TOTAL: $TOTAL / PASS: $PASS / FAIL: $FAIL / SKIP: $SKIP"
|
||||||
|
echo "${output}"
|
||||||
|
|
||||||
|
if [[ "$FAIL" -gt 0 ]]; then
|
||||||
|
echo "::warning ::${output}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
jq -n \
|
||||||
|
--arg date "$(date --rfc-email)" \
|
||||||
|
--arg sha "$GITHUB_SHA" \
|
||||||
|
--arg total "$TOTAL" \
|
||||||
|
--arg pass "$PASS" \
|
||||||
|
--arg skip "$SKIP" \
|
||||||
|
--arg fail "$FAIL" \
|
||||||
|
--arg error "$ERROR" \
|
||||||
|
'{($date): { sha: $sha, total: $total, pass: $pass, skip: $skip, fail: $fail, error: $error }}' > '${{ steps.vars.outputs.TEST_SUMMARY_FILE }}'
|
||||||
|
|
||||||
|
HASH=$(sha1sum '${{ steps.vars.outputs.TEST_SUMMARY_FILE }}' | cut --delim=" " -f 1)
|
||||||
|
outputs HASH TOTAL PASS FAIL SKIP
|
||||||
|
|
||||||
|
- name: Upload SHA1/ID of 'test-summary'
|
||||||
|
uses: actions/upload-artifact@v6
|
||||||
|
with:
|
||||||
|
name: "${{ steps.summary.outputs.HASH }}"
|
||||||
|
path: "${{ steps.vars.outputs.TEST_SUMMARY_FILE }}"
|
||||||
|
|
||||||
|
- name: Upload test results summary
|
||||||
|
uses: actions/upload-artifact@v6
|
||||||
|
with:
|
||||||
|
name: test-summary
|
||||||
|
path: "${{ steps.vars.outputs.TEST_SUMMARY_FILE }}"
|
||||||
|
|
||||||
|
- name: Compare test failures VS reference
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
## Compare test failures VS reference using JSON files
|
||||||
|
REF_SUMMARY_FILE='reference/sed-gnu-full-result/sed-gnu-full-result.json'
|
||||||
|
CURRENT_SUMMARY_FILE="results/${{ env.TEST_FULL_SUMMARY_FILE }}"
|
||||||
|
REPO_DEFAULT_BRANCH='${{ env.DEFAULT_BRANCH }}'
|
||||||
|
|
||||||
|
# Path to ignore file for intermittent issues
|
||||||
|
IGNORE_INTERMITTENT="sed/.github/workflows/ignore-intermittent.txt"
|
||||||
|
|
||||||
|
# Set up comment directory
|
||||||
|
COMMENT_DIR="reference/comment"
|
||||||
|
mkdir -p ${COMMENT_DIR}
|
||||||
|
echo ${{ github.event.number }} > ${COMMENT_DIR}/NR
|
||||||
|
COMMENT_LOG="${COMMENT_DIR}/result.txt"
|
||||||
|
|
||||||
|
COMPARISON_RESULT=0
|
||||||
|
if test -f "${CURRENT_SUMMARY_FILE}"; then
|
||||||
|
if test -f "${REF_SUMMARY_FILE}"; then
|
||||||
|
echo "Reference summary SHA1/ID: $(sha1sum -- "${REF_SUMMARY_FILE}")"
|
||||||
|
echo "Current summary SHA1/ID: $(sha1sum -- "${CURRENT_SUMMARY_FILE}")"
|
||||||
|
|
||||||
|
python3 sed/util/compare_test_results.py \
|
||||||
|
--ignore-file "${IGNORE_INTERMITTENT}" \
|
||||||
|
--output "${COMMENT_LOG}" \
|
||||||
|
"${CURRENT_SUMMARY_FILE}" "${REF_SUMMARY_FILE}"
|
||||||
|
|
||||||
|
COMPARISON_RESULT=$?
|
||||||
|
else
|
||||||
|
echo "::warning ::Skipping test comparison; no prior reference summary is available at '${REF_SUMMARY_FILE}'."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "::error ::Failed to find summary of test results (missing '${CURRENT_SUMMARY_FILE}'); failing early"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ${COMPARISON_RESULT} -eq 1 ]; then
|
||||||
|
echo "ONLY_INTERMITTENT=false" >> $GITHUB_ENV
|
||||||
|
echo "::error ::Found new non-intermittent test failures"
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
echo "ONLY_INTERMITTENT=true" >> $GITHUB_ENV
|
||||||
|
echo "::notice ::No new test failures detected"
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Upload comparison log (for GnuComment workflow)
|
||||||
|
if: success() || failure()
|
||||||
|
uses: actions/upload-artifact@v6
|
||||||
|
with:
|
||||||
|
name: comment
|
||||||
|
path: reference/comment/
|
||||||
|
|
||||||
|
- name: Report test results
|
||||||
|
if: success() || failure()
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
## Report final results
|
||||||
|
echo "::notice ::GNU sed testsuite results:"
|
||||||
|
echo "::notice :: Total tests: ${{ steps.summary.outputs.TOTAL }}"
|
||||||
|
echo "::notice :: Passed: ${{ steps.summary.outputs.PASS }}"
|
||||||
|
echo "::notice :: Failed: ${{ steps.summary.outputs.FAIL }}"
|
||||||
|
echo "::notice :: Skipped: ${{ steps.summary.outputs.SKIP }}"
|
||||||
|
|
||||||
|
if [[ "${{ steps.summary.outputs.FAIL }}" -gt 0 ]]; then
|
||||||
|
PASS_RATE=$(( ${{ steps.summary.outputs.PASS }} * 100 / (${{ steps.summary.outputs.PASS }} + ${{ steps.summary.outputs.FAIL }}) ))
|
||||||
|
echo "::notice :: Pass rate: ${PASS_RATE}%"
|
||||||
|
fi
|
||||||
@@ -69,9 +69,8 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
## `cargo clippy` lint testing
|
## `cargo clippy` lint testing
|
||||||
unset fault
|
unset fault
|
||||||
CLIPPY_FLAGS="-W clippy::default_trait_access -W clippy::manual_string_new -W clippy::cognitive_complexity -W clippy::implicit_clone -W clippy::range-plus-one -W clippy::redundant-clone -W clippy::match_bool -W clippy::semicolon_if_nothing_returned"
|
|
||||||
fault_type="${{ steps.vars.outputs.FAULT_TYPE }}"
|
fault_type="${{ steps.vars.outputs.FAULT_TYPE }}"
|
||||||
fault_prefix=$(echo "$fault_type" | tr '[:lower:]' '[:upper:]')
|
fault_prefix=$(echo "$fault_type" | tr '[:lower:]' '[:upper:]')
|
||||||
# * convert any warnings to GHA UI annotations; ref: <https://help.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-a-warning-message>
|
# * convert any warnings to GHA UI annotations; ref: <https://help.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-a-warning-message>
|
||||||
S=$(cargo clippy --all-targets --workspace -psed -- ${CLIPPY_FLAGS} -D warnings 2>&1) && printf "%s\n" "$S" || { printf "%s\n" "$S" ; printf "%s" "$S" | sed -E -n -e '/^error:/{' -e "N; s/^error:[[:space:]]+(.*)\\n[[:space:]]+-->[[:space:]]+(.*):([0-9]+):([0-9]+).*$/::${fault_type} file=\2,line=\3,col=\4::${fault_prefix}: \`cargo clippy\`: \1 (file:'\2', line:\3)/p;" -e '}' ; fault=true ; }
|
S=$(cargo clippy --all-targets --workspace -psed -- -D warnings 2>&1) && printf "%s\n" "$S" || { printf "%s\n" "$S" ; printf "%s" "$S" | sed -E -n -e '/^error:/{' -e "N; s/^error:[[:space:]]+(.*)\\n[[:space:]]+-->[[:space:]]+(.*):([0-9]+):([0-9]+).*$/::${fault_type} file=\2,line=\3,col=\4::${fault_prefix}: \`cargo clippy\`: \1 (file:'\2', line:\3)/p;" -e '}' ; fault=true ; }
|
||||||
if [ -n "${{ steps.vars.outputs.FAIL_ON_FAULT }}" ] && [ -n "$fault" ]; then exit 1 ; fi
|
if [ -n "${{ steps.vars.outputs.FAIL_ON_FAULT }}" ] && [ -n "$fault" ]; then exit 1 ; fi
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# List of intermittent test names to ignore in result comparisons
|
||||||
|
# Format: one test name per line, lines starting with # are comments
|
||||||
|
#
|
||||||
|
# Add test names that are known to be flaky or environment-dependent
|
||||||
|
# Example:
|
||||||
|
# basic_substitution
|
||||||
|
# line_address_test
|
||||||
Generated
+128
-60
@@ -56,7 +56,7 @@ version = "1.1.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
|
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-sys 0.61.2",
|
"windows-sys 0.60.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -67,7 +67,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"anstyle",
|
"anstyle",
|
||||||
"once_cell_polyfill",
|
"once_cell_polyfill",
|
||||||
"windows-sys 0.61.2",
|
"windows-sys 0.60.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -127,6 +127,15 @@ version = "2.10.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
|
checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "block-buffer"
|
||||||
|
version = "0.10.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bstr"
|
name = "bstr"
|
||||||
version = "1.12.1"
|
version = "1.12.1"
|
||||||
@@ -167,9 +176,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "chrono"
|
name = "chrono"
|
||||||
version = "0.4.42"
|
version = "0.4.43"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2"
|
checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"iana-time-zone",
|
"iana-time-zone",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
@@ -178,18 +187,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.5.53"
|
version = "4.5.55"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8"
|
checksum = "3e34525d5bbbd55da2bb745d34b36121baac88d07619a9a09cfcf4a6c0832785"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap_builder",
|
"clap_builder",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_builder"
|
name = "clap_builder"
|
||||||
version = "4.5.53"
|
version = "4.5.55"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00"
|
checksum = "59a20016a20a3da95bef50ec7238dbd09baeef4311dcdd38ec15aba69812fb61"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstream",
|
"anstream",
|
||||||
"anstyle",
|
"anstyle",
|
||||||
@@ -200,9 +209,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_complete"
|
name = "clap_complete"
|
||||||
version = "4.5.61"
|
version = "4.5.65"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "39615915e2ece2550c0149addac32fb5bd312c657f43845bb9088cb9c8a7c992"
|
checksum = "430b4dc2b5e3861848de79627b2bedc9f3342c7da5173a14eaa5d0f8dc18ae5d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
]
|
]
|
||||||
@@ -225,9 +234,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "codspeed"
|
name = "codspeed"
|
||||||
version = "4.1.0"
|
version = "4.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c3b847e05a34be5c38f3f2a5052178a3bd32e6b5702f3ea775efde95c483a539"
|
checksum = "38c2eb3388ebe26b5a0ab6bf4969d9c4840143d7f6df07caa3cc851b0606cef6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"cc",
|
"cc",
|
||||||
@@ -243,9 +252,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "codspeed-divan-compat"
|
name = "codspeed-divan-compat"
|
||||||
version = "4.1.0"
|
version = "4.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f0f0e9fe5eaa39995ec35e46407f7154346cc25bd1300c64c21636f3d00cb2cc"
|
checksum = "b2de65b7489a59709724d489070c6d05b7744039e4bf751d0a2006b90bb5593d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"codspeed",
|
"codspeed",
|
||||||
@@ -256,9 +265,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "codspeed-divan-compat-macros"
|
name = "codspeed-divan-compat-macros"
|
||||||
version = "4.1.0"
|
version = "4.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "88c8babf2a40fd2206a2e030cf020d0d58144cd56e1dc408bfba02cdefb08b4f"
|
checksum = "56ca01ce4fd22b8dcc6c770dcd6b74343642e842482b94e8920d14e10c57638d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"divan-macros",
|
"divan-macros",
|
||||||
"itertools",
|
"itertools",
|
||||||
@@ -270,9 +279,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "codspeed-divan-compat-walltime"
|
name = "codspeed-divan-compat-walltime"
|
||||||
version = "4.1.0"
|
version = "4.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7f26092328e12a36704ffc552f379c6405dd94d3149970b79b22d371717c2aae"
|
checksum = "720ab9d0714718afe5f5832be6e5f5eb5ce97836e24ca7bf7042eea4308b9fb8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"clap",
|
"clap",
|
||||||
@@ -311,6 +320,15 @@ version = "0.8.7"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cpufeatures"
|
||||||
|
version = "0.2.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-deque"
|
name = "crossbeam-deque"
|
||||||
version = "0.8.6"
|
version = "0.8.6"
|
||||||
@@ -336,6 +354,16 @@ version = "0.8.21"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crypto-common"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
"typenum",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ctor"
|
name = "ctor"
|
||||||
version = "0.6.3"
|
version = "0.6.3"
|
||||||
@@ -373,6 +401,16 @@ version = "0.4.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8"
|
checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8"
|
||||||
|
|
||||||
|
[[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]]
|
[[package]]
|
||||||
name = "displaydoc"
|
name = "displaydoc"
|
||||||
version = "0.2.5"
|
version = "0.2.5"
|
||||||
@@ -447,7 +485,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"windows-sys 0.61.2",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -527,6 +565,16 @@ dependencies = [
|
|||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[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]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.2.16"
|
version = "0.2.16"
|
||||||
@@ -586,6 +634,12 @@ version = "0.16.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hex"
|
||||||
|
version = "0.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "iana-time-zone"
|
name = "iana-time-zone"
|
||||||
version = "0.1.64"
|
version = "0.1.64"
|
||||||
@@ -598,7 +652,7 @@ dependencies = [
|
|||||||
"js-sys",
|
"js-sys",
|
||||||
"log",
|
"log",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
"windows-core 0.62.2",
|
"windows-core 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -688,7 +742,7 @@ dependencies = [
|
|||||||
"portable-atomic",
|
"portable-atomic",
|
||||||
"portable-atomic-util",
|
"portable-atomic-util",
|
||||||
"serde_core",
|
"serde_core",
|
||||||
"windows-sys 0.61.2",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -735,9 +789,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.178"
|
version = "0.2.180"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091"
|
checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linux-raw-sys"
|
name = "linux-raw-sys"
|
||||||
@@ -1086,15 +1140,15 @@ checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "1.1.2"
|
version = "1.1.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e"
|
checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"errno",
|
"errno",
|
||||||
"libc",
|
"libc",
|
||||||
"linux-raw-sys",
|
"linux-raw-sys",
|
||||||
"windows-sys 0.61.2",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1130,10 +1184,10 @@ dependencies = [
|
|||||||
"codspeed-divan-compat",
|
"codspeed-divan-compat",
|
||||||
"ctor",
|
"ctor",
|
||||||
"fancy-regex",
|
"fancy-regex",
|
||||||
|
"hex",
|
||||||
"libc",
|
"libc",
|
||||||
"memchr",
|
"memchr",
|
||||||
"memmap2",
|
"memmap2",
|
||||||
"once_cell",
|
|
||||||
"phf",
|
"phf",
|
||||||
"phf_codegen",
|
"phf_codegen",
|
||||||
"predicates",
|
"predicates",
|
||||||
@@ -1141,6 +1195,7 @@ dependencies = [
|
|||||||
"rand",
|
"rand",
|
||||||
"regex",
|
"regex",
|
||||||
"rlimit",
|
"rlimit",
|
||||||
|
"sha2",
|
||||||
"sysinfo",
|
"sysinfo",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"terminal_size",
|
"terminal_size",
|
||||||
@@ -1199,6 +1254,17 @@ dependencies = [
|
|||||||
"serde_core",
|
"serde_core",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sha2"
|
||||||
|
version = "0.10.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"cpufeatures",
|
||||||
|
"digest",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "shlex"
|
name = "shlex"
|
||||||
version = "1.3.0"
|
version = "1.3.0"
|
||||||
@@ -1262,9 +1328,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sysinfo"
|
name = "sysinfo"
|
||||||
version = "0.37.2"
|
version = "0.38.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "16607d5caffd1c07ce073528f9ed972d88db15dd44023fa57142963be3feb11f"
|
checksum = "fe840c5b1afe259a5657392a4dbb74473a14c8db999c3ec2f4ae812e028a94da"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"memchr",
|
"memchr",
|
||||||
@@ -1276,15 +1342,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tempfile"
|
name = "tempfile"
|
||||||
version = "3.23.0"
|
version = "3.24.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16"
|
checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"fastrand",
|
"fastrand",
|
||||||
"getrandom 0.3.4",
|
"getrandom 0.3.4",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"rustix",
|
"rustix",
|
||||||
"windows-sys 0.61.2",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1418,6 +1484,12 @@ dependencies = [
|
|||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typenum"
|
||||||
|
version = "1.19.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unic-langid"
|
name = "unic-langid"
|
||||||
version = "0.9.6"
|
version = "0.9.6"
|
||||||
@@ -1513,6 +1585,12 @@ dependencies = [
|
|||||||
"xattr",
|
"xattr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version_check"
|
||||||
|
version = "0.9.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "walkdir"
|
name = "walkdir"
|
||||||
version = "2.5.0"
|
version = "2.5.0"
|
||||||
@@ -1614,7 +1692,7 @@ version = "0.1.11"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-sys 0.61.2",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1625,24 +1703,23 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows"
|
name = "windows"
|
||||||
version = "0.61.3"
|
version = "0.62.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893"
|
checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-collections",
|
"windows-collections",
|
||||||
"windows-core 0.61.2",
|
"windows-core 0.62.2",
|
||||||
"windows-future",
|
"windows-future",
|
||||||
"windows-link 0.1.3",
|
|
||||||
"windows-numerics",
|
"windows-numerics",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-collections"
|
name = "windows-collections"
|
||||||
version = "0.2.0"
|
version = "0.3.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8"
|
checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-core 0.61.2",
|
"windows-core 0.62.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1673,12 +1750,12 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-future"
|
name = "windows-future"
|
||||||
version = "0.2.1"
|
version = "0.3.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e"
|
checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-core 0.61.2",
|
"windows-core 0.62.2",
|
||||||
"windows-link 0.1.3",
|
"windows-link 0.2.1",
|
||||||
"windows-threading",
|
"windows-threading",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1718,12 +1795,12 @@ checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-numerics"
|
name = "windows-numerics"
|
||||||
version = "0.2.0"
|
version = "0.3.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1"
|
checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-core 0.61.2",
|
"windows-core 0.62.2",
|
||||||
"windows-link 0.1.3",
|
"windows-link 0.2.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1780,15 +1857,6 @@ dependencies = [
|
|||||||
"windows-targets 0.53.5",
|
"windows-targets 0.53.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-sys"
|
|
||||||
version = "0.61.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
|
||||||
dependencies = [
|
|
||||||
"windows-link 0.2.1",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-targets"
|
name = "windows-targets"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
@@ -1824,11 +1892,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-threading"
|
name = "windows-threading"
|
||||||
version = "0.1.0"
|
version = "0.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6"
|
checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-link 0.1.3",
|
"windows-link 0.2.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
+15
-8
@@ -38,16 +38,17 @@ clap_complete = "4.5"
|
|||||||
clap_mangen = "0.2"
|
clap_mangen = "0.2"
|
||||||
divan = { package = "codspeed-divan-compat", version = "4.0.5" }
|
divan = { package = "codspeed-divan-compat", version = "4.0.5" }
|
||||||
fancy-regex = "0.17.0"
|
fancy-regex = "0.17.0"
|
||||||
|
hex = "0.4"
|
||||||
libc = "0.2.153"
|
libc = "0.2.153"
|
||||||
memchr = "2.7.4"
|
memchr = "2.7.4"
|
||||||
memmap2 = "0.9"
|
memmap2 = "0.9"
|
||||||
once_cell = "1.21.3"
|
|
||||||
phf = "0.13.0"
|
phf = "0.13.0"
|
||||||
phf_codegen = "0.13.0"
|
phf_codegen = "0.13.0"
|
||||||
predicates = "3.1.3"
|
predicates = "3.1.3"
|
||||||
rand = { version = "0.9", features = ["small_rng"] }
|
rand = { version = "0.9", features = ["small_rng"] }
|
||||||
regex = "1.10.4"
|
regex = "1.10.4"
|
||||||
sysinfo = "0.37"
|
sha2 = "0.10"
|
||||||
|
sysinfo = "0.38"
|
||||||
tempfile = "3.10.1"
|
tempfile = "3.10.1"
|
||||||
textwrap = { version = "0.16.1", features = ["terminal_size"] }
|
textwrap = { version = "0.16.1", features = ["terminal_size"] }
|
||||||
terminal_size = "0.4.2"
|
terminal_size = "0.4.2"
|
||||||
@@ -64,7 +65,6 @@ ctor = "0.6.0"
|
|||||||
fancy-regex = { workspace = true }
|
fancy-regex = { workspace = true }
|
||||||
memchr = { workspace = true }
|
memchr = { workspace = true }
|
||||||
memmap2.workspace = true
|
memmap2.workspace = true
|
||||||
once_cell = { workspace = true }
|
|
||||||
phf = { workspace = true }
|
phf = { workspace = true }
|
||||||
predicates = { workspace = true }
|
predicates = { workspace = true }
|
||||||
regex = { workspace = true }
|
regex = { workspace = true }
|
||||||
@@ -77,10 +77,12 @@ uucore = { workspace = true }
|
|||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
chrono = { workspace = true }
|
chrono = { workspace = true }
|
||||||
divan = { workspace = true }
|
divan = { workspace = true }
|
||||||
|
hex = { workspace = true }
|
||||||
libc = { workspace = true }
|
libc = { workspace = true }
|
||||||
pretty_assertions = "1"
|
pretty_assertions = "1"
|
||||||
rand = { workspace = true }
|
rand = { workspace = true }
|
||||||
regex = { workspace = true }
|
regex = { workspace = true }
|
||||||
|
sha2 = { workspace = true }
|
||||||
tempfile = { workspace = true }
|
tempfile = { workspace = true }
|
||||||
uucore = { workspace = true, features = ["entries", "process", "signals", "benchmark"] }
|
uucore = { workspace = true, features = ["entries", "process", "signals", "benchmark"] }
|
||||||
uutests = "0.5.0"
|
uutests = "0.5.0"
|
||||||
@@ -116,7 +118,7 @@ lto = true
|
|||||||
[profile.release-fast]
|
[profile.release-fast]
|
||||||
inherits = "release"
|
inherits = "release"
|
||||||
panic = "abort"
|
panic = "abort"
|
||||||
codegen-units = 1
|
codegen-units = 1 # should be moved to release without regression
|
||||||
|
|
||||||
# A release-like profile that is as small as possible.
|
# A release-like profile that is as small as possible.
|
||||||
[profile.release-small]
|
[profile.release-small]
|
||||||
@@ -124,7 +126,12 @@ inherits = "release-fast"
|
|||||||
opt-level = "z"
|
opt-level = "z"
|
||||||
strip = true
|
strip = true
|
||||||
|
|
||||||
# The profile that 'cargo dist' will build with
|
[lints.clippy]
|
||||||
[profile.dist]
|
default_trait_access = "warn"
|
||||||
inherits = "release"
|
manual_string_new = "warn"
|
||||||
lto = "thin"
|
cognitive_complexity = "warn"
|
||||||
|
implicit_clone = "warn"
|
||||||
|
range-plus-one = "warn"
|
||||||
|
redundant-clone = "warn"
|
||||||
|
match_bool = "warn"
|
||||||
|
semicolon_if_nothing_returned = "warn"
|
||||||
|
|||||||
@@ -41,6 +41,31 @@ cargo run --release
|
|||||||
|
|
||||||
The binary is named `sed` in `target/release/sed`.
|
The binary is named `sed` in `target/release/sed`.
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
### GNU sed Compatibility Testing
|
||||||
|
|
||||||
|
Test compatibility against GNU sed using the comprehensive testsuite (47+ tests, ~10% pass rate):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Clone GNU sed testsuite (one time setup)
|
||||||
|
git clone https://github.com/mirror/sed.git ../gnu.sed
|
||||||
|
|
||||||
|
# Run compatibility tests
|
||||||
|
./util/run-gnu-testsuite.sh
|
||||||
|
|
||||||
|
# Generate JSON results for CI
|
||||||
|
./util/run-gnu-testsuite.sh --json-output results.json
|
||||||
|
```
|
||||||
|
|
||||||
|
The testsuite extracts test cases from the GNU sed repository and tests them against expected outputs.
|
||||||
|
|
||||||
|
### Unit Tests
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo test
|
||||||
|
```
|
||||||
|
|
||||||
## Extensions and incompatibilities
|
## Extensions and incompatibilities
|
||||||
### Supported GNU extensions
|
### Supported GNU extensions
|
||||||
* Command-line arguments can be specified in long (`--`) form.
|
* Command-line arguments can be specified in long (`--`) form.
|
||||||
@@ -58,6 +83,10 @@ The binary is named `sed` in `target/release/sed`.
|
|||||||
* The `q` command can be optionally followed by an exit code.
|
* The `q` command can be optionally followed by an exit code.
|
||||||
* The `l` command can be optionally followed by the output width.
|
* The `l` command can be optionally followed by the output width.
|
||||||
* The `--follow-symlinks` flag for in-place editing.
|
* The `--follow-symlinks` flag for in-place editing.
|
||||||
|
* Address 0 can be used to specify an address range that is already
|
||||||
|
active on line 1 and can finish with the specified regular expression.
|
||||||
|
* Address steps can be specified in the form of start~step and start,~step
|
||||||
|
ranges.
|
||||||
|
|
||||||
### Supported BSD and GNU extensions
|
### Supported BSD and GNU extensions
|
||||||
* The second address in a range can be specified as a relative address with +N.
|
* The second address in a range can be specified as a relative address with +N.
|
||||||
@@ -87,6 +116,13 @@ The binary is named `sed` in `target/release/sed`.
|
|||||||
* Labels are parsed for alphanumeric characters. The BSD version parses them
|
* Labels are parsed for alphanumeric characters. The BSD version parses them
|
||||||
until the end of the line, preventing ; to be used as a separator.
|
until the end of the line, preventing ; to be used as a separator.
|
||||||
|
|
||||||
|
## GNU test suite compatibility
|
||||||
|
|
||||||
|
Below is the evolution of how many GNU tests uutils passes.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
sed is licensed under the MIT License - see the `LICENSE` file for details
|
sed is licensed under the MIT License - see the `LICENSE` file for details
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ fn no_op_short(bencher: Bencher) {
|
|||||||
let temp_dir = tempfile::tempdir().unwrap();
|
let temp_dir = tempfile::tempdir().unwrap();
|
||||||
let mut data = Vec::new();
|
let mut data = Vec::new();
|
||||||
for i in 0..10_000_000 {
|
for i in 0..10_000_000 {
|
||||||
data.extend_from_slice(format!("{}\n", i).as_bytes());
|
data.extend_from_slice(format!("{i}\n").as_bytes());
|
||||||
}
|
}
|
||||||
let file_path = create_test_file(&data, temp_dir.path());
|
let file_path = create_test_file(&data, temp_dir.path());
|
||||||
let file_path_str = file_path.to_str().unwrap();
|
let file_path_str = file_path.to_str().unwrap();
|
||||||
@@ -192,7 +192,7 @@ fn remove_cr(bencher: Bencher) {
|
|||||||
let temp_dir = tempfile::tempdir().unwrap();
|
let temp_dir = tempfile::tempdir().unwrap();
|
||||||
let mut data = Vec::new();
|
let mut data = Vec::new();
|
||||||
for i in 0..500_000 {
|
for i in 0..500_000 {
|
||||||
let line = format!("line {} with windows endings\r\n", i);
|
let line = format!("line {i} with windows endings\r\n");
|
||||||
data.extend_from_slice(line.as_bytes());
|
data.extend_from_slice(line.as_bytes());
|
||||||
}
|
}
|
||||||
let file_path = create_test_file(&data, temp_dir.path());
|
let file_path = create_test_file(&data, temp_dir.path());
|
||||||
@@ -240,7 +240,7 @@ fn number_fix(bencher: Bencher) {
|
|||||||
let cents = i % 100;
|
let cents = i % 100;
|
||||||
let thousands = euros / 1000;
|
let thousands = euros / 1000;
|
||||||
let remainder = euros % 1000;
|
let remainder = euros % 1000;
|
||||||
let line = format!("{}.{:03},{:02}\n", thousands, remainder, cents);
|
let line = format!("{thousands}.{remainder:03},{cents:02}\n");
|
||||||
data.extend_from_slice(line.as_bytes());
|
data.extend_from_slice(line.as_bytes());
|
||||||
}
|
}
|
||||||
let file_path = create_test_file(&data, temp_dir.path());
|
let file_path = create_test_file(&data, temp_dir.path());
|
||||||
|
|||||||
Generated
+11
-12
@@ -579,9 +579,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.178"
|
version = "0.2.180"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091"
|
checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libfuzzer-sys"
|
name = "libfuzzer-sys"
|
||||||
@@ -601,9 +601,9 @@ checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linux-raw-sys"
|
name = "linux-raw-sys"
|
||||||
version = "0.9.4"
|
version = "0.11.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
|
checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
@@ -923,15 +923,15 @@ checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "1.0.7"
|
version = "1.1.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266"
|
checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"errno",
|
"errno",
|
||||||
"libc",
|
"libc",
|
||||||
"linux-raw-sys",
|
"linux-raw-sys",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.61.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -951,7 +951,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sed"
|
name = "sed"
|
||||||
version = "0.0.1"
|
version = "0.1.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"assert_fs",
|
"assert_fs",
|
||||||
"clap",
|
"clap",
|
||||||
@@ -961,7 +961,6 @@ dependencies = [
|
|||||||
"fancy-regex",
|
"fancy-regex",
|
||||||
"memchr",
|
"memchr",
|
||||||
"memmap2",
|
"memmap2",
|
||||||
"once_cell",
|
|
||||||
"phf",
|
"phf",
|
||||||
"phf_codegen",
|
"phf_codegen",
|
||||||
"predicates",
|
"predicates",
|
||||||
@@ -1068,15 +1067,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tempfile"
|
name = "tempfile"
|
||||||
version = "3.23.0"
|
version = "3.24.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16"
|
checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"fastrand",
|
"fastrand",
|
||||||
"getrandom",
|
"getrandom",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"rustix",
|
"rustix",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.61.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
+11
-20
@@ -64,6 +64,8 @@ pub struct ProcessingContext {
|
|||||||
pub parsed_block_nesting: usize,
|
pub parsed_block_nesting: usize,
|
||||||
/// Command associated with each label
|
/// Command associated with each label
|
||||||
pub label_to_command_map: HashMap<String, Rc<RefCell<Command>>>,
|
pub label_to_command_map: HashMap<String, Rc<RefCell<Command>>>,
|
||||||
|
/// Commands with a (latchable and resetable) address range
|
||||||
|
pub range_commands: Vec<Rc<RefCell<Command>>>,
|
||||||
/// True if a substitution was made as specified in the t command
|
/// True if a substitution was made as specified in the t command
|
||||||
pub substitution_made: bool,
|
pub substitution_made: bool,
|
||||||
/// Elements to append at the end of each command processing cycle
|
/// Elements to append at the end of each command processing cycle
|
||||||
@@ -84,26 +86,15 @@ pub struct StringSpace {
|
|||||||
pub has_newline: bool, // True if \n-terminated
|
pub has_newline: bool, // True if \n-terminated
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug)]
|
||||||
/// Types of address specifications that precede commands
|
/// Types of address specifications that precede commands
|
||||||
pub enum AddressType {
|
pub enum Address {
|
||||||
Re, // Line that matches regex
|
Re(Option<Regex>), // Line that matches (optional) regex
|
||||||
Line, // Specific line
|
Line(usize), // Specific line
|
||||||
RelLine, // Relative line
|
RelLine(usize), // Relative line
|
||||||
Last, // Last line
|
Last, // Last line
|
||||||
}
|
StepMatch(usize), // Lines matching specified step from first
|
||||||
|
StepEnd(usize), // Range ending at specified step from first
|
||||||
#[derive(Debug)]
|
|
||||||
/// Format of an address
|
|
||||||
pub struct Address {
|
|
||||||
pub atype: AddressType, // Address type
|
|
||||||
pub value: AddressValue, // Line number or regex
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum AddressValue {
|
|
||||||
LineNumber(usize),
|
|
||||||
Regex(Option<Regex>),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -278,7 +269,7 @@ pub struct Command {
|
|||||||
pub addr1: Option<Address>, // Start address
|
pub addr1: Option<Address>, // Start address
|
||||||
pub addr2: Option<Address>, // End address
|
pub addr2: Option<Address>, // End address
|
||||||
pub non_select: bool, // True if '!'
|
pub non_select: bool, // True if '!'
|
||||||
pub start_line: Option<usize>, // Start line number (or None)
|
pub start_line: Option<usize>, // Start line number (or None if unlatched)
|
||||||
pub data: CommandData, // Command-specific data
|
pub data: CommandData, // Command-specific data
|
||||||
pub next: Option<Rc<RefCell<Command>>>, // Pointer to next command
|
pub next: Option<Rc<RefCell<Command>>>, // Pointer to next command
|
||||||
pub location: ScriptLocation, // Command's definition location
|
pub location: ScriptLocation, // Command's definition location
|
||||||
|
|||||||
+297
-161
File diff suppressed because it is too large
Load Diff
+87
-88
@@ -61,7 +61,6 @@ pub struct MmapLineCursor<'a> {
|
|||||||
pub struct NextMmapLine<'a> {
|
pub struct NextMmapLine<'a> {
|
||||||
pub content: &'a [u8],
|
pub content: &'a [u8],
|
||||||
pub full_span: &'a [u8],
|
pub full_span: &'a [u8],
|
||||||
pub is_last_line: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
@@ -76,7 +75,7 @@ impl<'a> MmapLineCursor<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Return the next line, if available, or None.
|
/// Return the next line, if available, or None.
|
||||||
fn get_line(&mut self) -> io::Result<Option<NextMmapLine<'_>>> {
|
fn get_line(&mut self) -> io::Result<Option<NextMmapLine<'a>>> {
|
||||||
if self.pos >= self.data.len() {
|
if self.pos >= self.data.len() {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
@@ -101,12 +100,12 @@ impl<'a> MmapLineCursor<'a> {
|
|||||||
full_span
|
full_span
|
||||||
};
|
};
|
||||||
|
|
||||||
let is_last_line = self.pos >= self.data.len();
|
Ok(Some(NextMmapLine { content, full_span }))
|
||||||
Ok(Some(NextMmapLine {
|
}
|
||||||
content,
|
|
||||||
full_span,
|
/// Return true if the previously returned line was the last one.
|
||||||
is_last_line,
|
fn last_line(&mut self) -> io::Result<bool> {
|
||||||
}))
|
Ok(self.pos >= self.data.len())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,9 +125,8 @@ impl ReadLineCursor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If a line is available, return it, its \n termination,
|
/// If a line is available, return it and its \n termination.
|
||||||
/// and next line availability, otherwise return None.
|
fn get_line(&mut self) -> io::Result<Option<(String, bool)>> {
|
||||||
fn get_line(&mut self) -> io::Result<Option<(String, bool, bool)>> {
|
|
||||||
self.buffer.clear();
|
self.buffer.clear();
|
||||||
// read_line *includes* the '\n' if present
|
// read_line *includes* the '\n' if present
|
||||||
let bytes_read = self.reader.read_line(&mut self.buffer)?;
|
let bytes_read = self.reader.read_line(&mut self.buffer)?;
|
||||||
@@ -142,8 +140,14 @@ impl ReadLineCursor {
|
|||||||
self.buffer.pop();
|
self.buffer.pop();
|
||||||
}
|
}
|
||||||
let line = std::mem::take(&mut self.buffer);
|
let line = std::mem::take(&mut self.buffer);
|
||||||
let is_last_line = self.reader.fill_buf()?.is_empty();
|
Ok(Some((line, has_newline)))
|
||||||
Ok(Some((line, has_newline, is_last_line)))
|
}
|
||||||
|
|
||||||
|
/// Return true if the previously returned line was the last one.
|
||||||
|
fn last_line(&mut self) -> io::Result<bool> {
|
||||||
|
// FIXME(rust-lang#86423): Replace with BufRead::has_data_left()
|
||||||
|
// when/if method becomes stable.
|
||||||
|
Ok(self.reader.fill_buf()?.is_empty())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -466,21 +470,15 @@ impl<'a> LineReader<'a> {
|
|||||||
line_reader_read_input(file)
|
line_reader_read_input(file)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the next line, if available and also the availability
|
/// Return the next line, if available.
|
||||||
/// of another one, or None at end of file.
|
pub fn get_line(&mut self) -> io::Result<Option<IOChunk<'a>>> {
|
||||||
pub fn get_line(&mut self) -> io::Result<Option<(IOChunk<'_>, bool)>> {
|
|
||||||
match self {
|
match self {
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
LineReader::MmapInput { cursor, .. } => {
|
LineReader::MmapInput { cursor, .. } => {
|
||||||
// Obtain fields to prevent borrowing issues.
|
// Obtain fields to prevent borrowing issues.
|
||||||
let fast_copy = cursor.fast_copy.clone();
|
let fast_copy = cursor.fast_copy.clone();
|
||||||
let base = cursor.data.as_ptr();
|
let base = cursor.data.as_ptr();
|
||||||
if let Some(NextMmapLine {
|
if let Some(NextMmapLine { content, full_span }) = cursor.get_line()? {
|
||||||
content,
|
|
||||||
full_span,
|
|
||||||
is_last_line,
|
|
||||||
}) = cursor.get_line()?
|
|
||||||
{
|
|
||||||
let chunk = IOChunk::from_content(IOChunkContent::MmapInput {
|
let chunk = IOChunk::from_content(IOChunkContent::MmapInput {
|
||||||
fast_copy,
|
fast_copy,
|
||||||
base,
|
base,
|
||||||
@@ -488,17 +486,17 @@ impl<'a> LineReader<'a> {
|
|||||||
full_span,
|
full_span,
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(Some((chunk, is_last_line)))
|
Ok(Some(chunk))
|
||||||
} else {
|
} else {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LineReader::ReadInput(cursor) => {
|
LineReader::ReadInput(cursor) => {
|
||||||
if let Some((line, _has_newline, is_last_line)) = cursor.get_line()? {
|
if let Some((line, _has_newline)) = cursor.get_line()? {
|
||||||
let chunk =
|
let chunk =
|
||||||
IOChunk::from_content(IOChunkContent::new_owned(line, _has_newline));
|
IOChunk::from_content(IOChunkContent::new_owned(line, _has_newline));
|
||||||
Ok(Some((chunk, is_last_line)))
|
Ok(Some(chunk))
|
||||||
} else {
|
} else {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
@@ -508,6 +506,19 @@ impl<'a> LineReader<'a> {
|
|||||||
LineReader::_Phantom(_) => unreachable!("_Phantom should never be constructed"),
|
LineReader::_Phantom(_) => unreachable!("_Phantom should never be constructed"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return true if the previously returned line was the last one.
|
||||||
|
pub fn last_line(&mut self) -> io::Result<bool> {
|
||||||
|
match self {
|
||||||
|
#[cfg(unix)]
|
||||||
|
LineReader::MmapInput { cursor, .. } => cursor.last_line(),
|
||||||
|
|
||||||
|
LineReader::ReadInput(cursor) => cursor.last_line(),
|
||||||
|
|
||||||
|
#[cfg(not(unix))]
|
||||||
|
LineReader::_Phantom(_) => unreachable!("_Phantom should never be constructed"),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Define a trait combining two: workaround for Rust's corresponding inability.
|
// Define a trait combining two: workaround for Rust's corresponding inability.
|
||||||
@@ -1048,7 +1059,7 @@ mod tests {
|
|||||||
let mut out = OutputBuffer::new(Box::new(Box::new(out_file)));
|
let mut out = OutputBuffer::new(Box::new(Box::new(out_file)));
|
||||||
|
|
||||||
// Drain reader → writer
|
// Drain reader → writer
|
||||||
while let Some((chunk, _last_line)) = reader.get_line()? {
|
while let Some(chunk) = reader.get_line()? {
|
||||||
out.write_chunk(&chunk)?;
|
out.write_chunk(&chunk)?;
|
||||||
}
|
}
|
||||||
out.flush()?;
|
out.flush()?;
|
||||||
@@ -1084,7 +1095,7 @@ mod tests {
|
|||||||
let mut out = OutputBuffer::new(Box::new(out_file));
|
let mut out = OutputBuffer::new(Box::new(out_file));
|
||||||
|
|
||||||
// Read the first mmap line ("zero\n") and write it
|
// Read the first mmap line ("zero\n") and write it
|
||||||
if let Some((chunk, _last_line)) = reader.get_line()? {
|
if let Some(chunk) = reader.get_line()? {
|
||||||
out.write_chunk(&chunk)?;
|
out.write_chunk(&chunk)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1092,7 +1103,7 @@ mod tests {
|
|||||||
out.write_str("middle\n")?;
|
out.write_str("middle\n")?;
|
||||||
|
|
||||||
// Read the second mmap line ("one\n") and write it
|
// Read the second mmap line ("one\n") and write it
|
||||||
if let Some((chunk, _last_line)) = reader.get_line()? {
|
if let Some(chunk) = reader.get_line()? {
|
||||||
out.write_chunk(&chunk)?;
|
out.write_chunk(&chunk)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1137,7 +1148,7 @@ mod tests {
|
|||||||
// Wrap it in your OutputBuffer and run the loop:
|
// Wrap it in your OutputBuffer and run the loop:
|
||||||
let mut out = OutputBuffer::new(Box::new(out_file));
|
let mut out = OutputBuffer::new(Box::new(out_file));
|
||||||
let mut nline = 0;
|
let mut nline = 0;
|
||||||
while let Some((chunk, _last_line)) = reader.get_line()? {
|
while let Some(chunk) = reader.get_line()? {
|
||||||
out.write_chunk(&chunk)?;
|
out.write_chunk(&chunk)?;
|
||||||
nline += 1;
|
nline += 1;
|
||||||
}
|
}
|
||||||
@@ -1176,7 +1187,7 @@ mod tests {
|
|||||||
// Wrap it in your OutputBuffer and run the loop:
|
// Wrap it in your OutputBuffer and run the loop:
|
||||||
let mut out = OutputBuffer::new(Box::new(out_file));
|
let mut out = OutputBuffer::new(Box::new(out_file));
|
||||||
let mut nline = 0;
|
let mut nline = 0;
|
||||||
while let Some((chunk, _last_line)) = reader.get_line()? {
|
while let Some(chunk) = reader.get_line()? {
|
||||||
out.write_chunk(&chunk)?;
|
out.write_chunk(&chunk)?;
|
||||||
nline += 1;
|
nline += 1;
|
||||||
}
|
}
|
||||||
@@ -1211,7 +1222,7 @@ mod tests {
|
|||||||
// Wrap it in your OutputBuffer and run the loop:
|
// Wrap it in your OutputBuffer and run the loop:
|
||||||
let mut out = OutputBuffer::new(Box::new(out_file));
|
let mut out = OutputBuffer::new(Box::new(out_file));
|
||||||
let mut nline = 0;
|
let mut nline = 0;
|
||||||
while let Some((chunk, _last_line)) = reader.get_line()? {
|
while let Some(chunk) = reader.get_line()? {
|
||||||
out.write_chunk(&chunk)?;
|
out.write_chunk(&chunk)?;
|
||||||
nline += 1;
|
nline += 1;
|
||||||
}
|
}
|
||||||
@@ -1246,7 +1257,7 @@ mod tests {
|
|||||||
// Wrap it in your OutputBuffer and run the loop:
|
// Wrap it in your OutputBuffer and run the loop:
|
||||||
let mut out = OutputBuffer::new(Box::new(out_file));
|
let mut out = OutputBuffer::new(Box::new(out_file));
|
||||||
let mut nline = 0;
|
let mut nline = 0;
|
||||||
while let Some((chunk, _last_line)) = reader.get_line()? {
|
while let Some(chunk) = reader.get_line()? {
|
||||||
out.write_chunk(&chunk)?;
|
out.write_chunk(&chunk)?;
|
||||||
nline += 1;
|
nline += 1;
|
||||||
}
|
}
|
||||||
@@ -1288,7 +1299,7 @@ mod tests {
|
|||||||
// Wrap it in OutputBuffer and run the loop:
|
// Wrap it in OutputBuffer and run the loop:
|
||||||
let mut out = OutputBuffer::new(Box::new(out_file));
|
let mut out = OutputBuffer::new(Box::new(out_file));
|
||||||
let mut nline_written = 0;
|
let mut nline_written = 0;
|
||||||
while let Some((chunk, _last_line)) = reader.get_line()? {
|
while let Some(chunk) = reader.get_line()? {
|
||||||
out.write_chunk(&chunk)?;
|
out.write_chunk(&chunk)?;
|
||||||
nline_written += 1;
|
nline_written += 1;
|
||||||
}
|
}
|
||||||
@@ -1319,52 +1330,46 @@ mod tests {
|
|||||||
let mut reader = LineReader::open_stream(&path)?;
|
let mut reader = LineReader::open_stream(&path)?;
|
||||||
|
|
||||||
// Verify the reader's operation
|
// Verify the reader's operation
|
||||||
if let Some((
|
if let Some(IOChunk {
|
||||||
IOChunk {
|
content:
|
||||||
content:
|
IOChunkContent::Owned {
|
||||||
IOChunkContent::Owned {
|
content,
|
||||||
content,
|
has_newline,
|
||||||
has_newline,
|
..
|
||||||
..
|
},
|
||||||
},
|
utf8_verified,
|
||||||
utf8_verified,
|
..
|
||||||
..
|
}) = reader.get_line()?
|
||||||
},
|
|
||||||
last_line,
|
|
||||||
)) = reader.get_line()?
|
|
||||||
{
|
{
|
||||||
assert_eq!(content, "first line");
|
assert_eq!(content, "first line");
|
||||||
assert_eq!(content.len(), 10);
|
assert_eq!(content.len(), 10);
|
||||||
assert!(has_newline);
|
assert!(has_newline);
|
||||||
assert!(!utf8_verified.get());
|
assert!(!utf8_verified.get());
|
||||||
assert!(!last_line);
|
assert!(!reader.last_line().unwrap());
|
||||||
} else {
|
} else {
|
||||||
panic!("Expected IOChunkContent::Owned");
|
panic!("Expected IOChunkContent::Owned");
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some((
|
if let Some(IOChunk {
|
||||||
IOChunk {
|
content:
|
||||||
content:
|
IOChunkContent::Owned {
|
||||||
IOChunkContent::Owned {
|
content,
|
||||||
content,
|
has_newline,
|
||||||
has_newline,
|
..
|
||||||
..
|
},
|
||||||
},
|
..
|
||||||
..
|
}) = reader.get_line()?
|
||||||
},
|
|
||||||
last_line,
|
|
||||||
)) = reader.get_line()?
|
|
||||||
{
|
{
|
||||||
assert_eq!(content, "second line");
|
assert_eq!(content, "second line");
|
||||||
assert!(has_newline);
|
assert!(has_newline);
|
||||||
assert!(!last_line);
|
assert!(!reader.last_line().unwrap());
|
||||||
} else {
|
} else {
|
||||||
panic!("Expected IOChunkContent::Owned");
|
panic!("Expected IOChunkContent::Owned");
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some((content, last_line)) = reader.get_line()? {
|
if let Some(content) = reader.get_line()? {
|
||||||
assert_eq!(content.as_str().unwrap(), "last line");
|
assert_eq!(content.as_str().unwrap(), "last line");
|
||||||
assert!(last_line);
|
assert!(reader.last_line().unwrap());
|
||||||
} else {
|
} else {
|
||||||
panic!("Expected IOChunk");
|
panic!("Expected IOChunk");
|
||||||
}
|
}
|
||||||
@@ -1386,52 +1391,46 @@ mod tests {
|
|||||||
let mut reader = LineReader::open(&path)?;
|
let mut reader = LineReader::open(&path)?;
|
||||||
|
|
||||||
// Verify the reader's operation
|
// Verify the reader's operation
|
||||||
if let Some((
|
if let Some(IOChunk {
|
||||||
IOChunk {
|
content:
|
||||||
content:
|
IOChunkContent::MmapInput {
|
||||||
IOChunkContent::MmapInput {
|
content, full_span, ..
|
||||||
content, full_span, ..
|
},
|
||||||
},
|
utf8_verified,
|
||||||
utf8_verified,
|
..
|
||||||
..
|
}) = reader.get_line()?
|
||||||
},
|
|
||||||
last_line,
|
|
||||||
)) = reader.get_line()?
|
|
||||||
{
|
{
|
||||||
assert_eq!(content, b"first line");
|
assert_eq!(content, b"first line");
|
||||||
assert_eq!(content.len(), 10);
|
assert_eq!(content.len(), 10);
|
||||||
assert_eq!(full_span, b"first line\n");
|
assert_eq!(full_span, b"first line\n");
|
||||||
assert!(!utf8_verified.get());
|
assert!(!utf8_verified.get());
|
||||||
assert!(!last_line);
|
assert!(!reader.last_line().unwrap());
|
||||||
} else {
|
} else {
|
||||||
panic!("Expected IOChunkContent::MapInput");
|
panic!("Expected IOChunkContent::MapInput");
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some((
|
if let Some(IOChunk {
|
||||||
IOChunk {
|
content:
|
||||||
content:
|
IOChunkContent::MmapInput {
|
||||||
IOChunkContent::MmapInput {
|
content, full_span, ..
|
||||||
content, full_span, ..
|
},
|
||||||
},
|
utf8_verified,
|
||||||
utf8_verified,
|
..
|
||||||
..
|
}) = reader.get_line()?
|
||||||
},
|
|
||||||
last_line,
|
|
||||||
)) = reader.get_line()?
|
|
||||||
{
|
{
|
||||||
assert_eq!(content, b"second line");
|
assert_eq!(content, b"second line");
|
||||||
assert_eq!(full_span, b"second line\n");
|
assert_eq!(full_span, b"second line\n");
|
||||||
assert!(!utf8_verified.get());
|
assert!(!utf8_verified.get());
|
||||||
assert!(!last_line);
|
assert!(!reader.last_line().unwrap());
|
||||||
} else {
|
} else {
|
||||||
panic!("Expected IOChunkContent::MapInput");
|
panic!("Expected IOChunkContent::MapInput");
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some((content, last_line)) = reader.get_line()? {
|
if let Some(content) = reader.get_line()? {
|
||||||
assert_eq!(content.as_bytes(), b"last line");
|
assert_eq!(content.as_bytes(), b"last line");
|
||||||
assert_eq!(content.as_str().unwrap(), "last line");
|
assert_eq!(content.as_str().unwrap(), "last line");
|
||||||
assert!(content.utf8_verified.get());
|
assert!(content.utf8_verified.get());
|
||||||
assert!(last_line);
|
assert!(reader.last_line().unwrap());
|
||||||
// Cached version
|
// Cached version
|
||||||
assert_eq!(content.as_str().unwrap(), "last line");
|
assert_eq!(content.as_str().unwrap(), "last line");
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -15,12 +15,12 @@ use fancy_regex::{
|
|||||||
CaptureMatches as FancyCaptureMatches, Captures as FancyCaptures, Regex as FancyRegex,
|
CaptureMatches as FancyCaptureMatches, Captures as FancyCaptures, Regex as FancyRegex,
|
||||||
};
|
};
|
||||||
use memchr::memmem;
|
use memchr::memmem;
|
||||||
use once_cell::sync::Lazy;
|
|
||||||
use regex::Regex as RustRegex;
|
use regex::Regex as RustRegex;
|
||||||
use regex::bytes::{
|
use regex::bytes::{
|
||||||
CaptureMatches as ByteCaptureMatches, Captures as ByteCaptures, Regex as ByteRegex,
|
CaptureMatches as ByteCaptureMatches, Captures as ByteCaptures, Regex as ByteRegex,
|
||||||
};
|
};
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
use std::sync::LazyLock;
|
||||||
use uucore::error::{UResult, USimpleError};
|
use uucore::error::{UResult, USimpleError};
|
||||||
|
|
||||||
use crate::sed::fast_io::IOChunk;
|
use crate::sed::fast_io::IOChunk;
|
||||||
@@ -32,7 +32,8 @@ use crate::sed::fast_io::IOChunk;
|
|||||||
// For example, r"\\1" and r"[\1]" will match, whereas only a number
|
// For example, r"\\1" and r"[\1]" will match, whereas only a number
|
||||||
// after an odd number of backslashes and outside a character class
|
// after an odd number of backslashes and outside a character class
|
||||||
// should match.
|
// should match.
|
||||||
static NEEDS_FANCY_RE: Lazy<RustRegex> = Lazy::new(|| regex::Regex::new(r"\\[1-9]").unwrap());
|
static NEEDS_FANCY_RE: LazyLock<RustRegex> =
|
||||||
|
LazyLock::new(|| regex::Regex::new(r"\\[1-9]").unwrap());
|
||||||
|
|
||||||
/// All characters signifying that the match must be handled by an RE
|
/// All characters signifying that the match must be handled by an RE
|
||||||
/// rather than by plain string pattern matching.
|
/// rather than by plain string pattern matching.
|
||||||
@@ -41,7 +42,7 @@ static NEEDS_FANCY_RE: Lazy<RustRegex> = Lazy::new(|| regex::Regex::new(r"\\[1-9
|
|||||||
// matching, because Regex always constructs an automaton and needs
|
// matching, because Regex always constructs an automaton and needs
|
||||||
// to handle state transitions, whereas plain string matching can
|
// to handle state transitions, whereas plain string matching can
|
||||||
// use tailored CPU string or vectored instructions.
|
// use tailored CPU string or vectored instructions.
|
||||||
static NEEDS_RE: Lazy<RustRegex> = Lazy::new(|| {
|
static NEEDS_RE: LazyLock<RustRegex> = LazyLock::new(|| {
|
||||||
regex::Regex::new(
|
regex::Regex::new(
|
||||||
r"(?x) # Turn on verbose mode
|
r"(?x) # Turn on verbose mode
|
||||||
( ^ # Non-escaped: i.e. at BOL
|
( ^ # Non-escaped: i.e. at BOL
|
||||||
|
|||||||
@@ -216,6 +216,7 @@ fn build_context(matches: &ArgMatches) -> ProcessingContext {
|
|||||||
hold: StringSpace::default(),
|
hold: StringSpace::default(),
|
||||||
parsed_block_nesting: 0,
|
parsed_block_nesting: 0,
|
||||||
label_to_command_map: HashMap::new(),
|
label_to_command_map: HashMap::new(),
|
||||||
|
range_commands: Vec::new(),
|
||||||
substitution_made: false,
|
substitution_made: false,
|
||||||
append_elements: Vec::new(),
|
append_elements: Vec::new(),
|
||||||
}
|
}
|
||||||
|
|||||||
+84
-63
@@ -9,8 +9,7 @@
|
|||||||
// file that was distributed with this source code.
|
// file that was distributed with this source code.
|
||||||
|
|
||||||
use crate::sed::command::{
|
use crate::sed::command::{
|
||||||
Address, AddressType, AddressValue, AppendElement, Command, CommandData, InputAction,
|
Address, AppendElement, Command, CommandData, InputAction, ProcessingContext, Transliteration,
|
||||||
ProcessingContext, Transliteration,
|
|
||||||
};
|
};
|
||||||
use crate::sed::error_handling::{ScriptLocation, input_runtime_error};
|
use crate::sed::error_handling::{ScriptLocation, input_runtime_error};
|
||||||
use crate::sed::fast_io::{IOChunk, LineReader, OutputBuffer};
|
use crate::sed::fast_io::{IOChunk, LineReader, OutputBuffer};
|
||||||
@@ -40,30 +39,21 @@ macro_rules! extract_variant {
|
|||||||
/// Return true if the passed address matches the current I/O context.
|
/// Return true if the passed address matches the current I/O context.
|
||||||
fn match_address(
|
fn match_address(
|
||||||
addr: &Address,
|
addr: &Address,
|
||||||
|
reader: &mut LineReader,
|
||||||
pattern: &mut IOChunk,
|
pattern: &mut IOChunk,
|
||||||
context: &mut ProcessingContext,
|
context: &mut ProcessingContext,
|
||||||
location: &ScriptLocation,
|
location: &ScriptLocation,
|
||||||
) -> UResult<bool> {
|
) -> UResult<bool> {
|
||||||
match addr.atype {
|
match addr {
|
||||||
AddressType::Re => {
|
Address::Re(re) => {
|
||||||
if let AddressValue::Regex(ref re) = addr.value {
|
let regex = re_or_saved_re(re, context, location)?;
|
||||||
let regex = re_or_saved_re(re, context, location)?;
|
match regex.is_match(pattern) {
|
||||||
match regex.is_match(pattern) {
|
Ok(result) => Ok(result),
|
||||||
Ok(result) => Ok(result),
|
Err(e) => input_runtime_error(location, context, e.to_string()),
|
||||||
Err(e) => input_runtime_error(location, context, e.to_string()),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Ok(false)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AddressType::Line => {
|
Address::Line(lineno) => Ok(context.line_number == *lineno),
|
||||||
if let AddressValue::LineNumber(lineno) = addr.value {
|
|
||||||
Ok(context.line_number == lineno)
|
|
||||||
} else {
|
|
||||||
Ok(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Recognize "$" as the last line of last file. This is consistent
|
// Recognize "$" as the last line of last file. This is consistent
|
||||||
// with the original 7th Research Edition implementation:
|
// with the original 7th Research Edition implementation:
|
||||||
@@ -71,7 +61,7 @@ fn match_address(
|
|||||||
// The FreeBSD version checked for subsequent empty files, but this
|
// The FreeBSD version checked for subsequent empty files, but this
|
||||||
// can lead to destructive reads (e.g. from named pipes),
|
// can lead to destructive reads (e.g. from named pipes),
|
||||||
// and is probably an overkill.
|
// and is probably an overkill.
|
||||||
AddressType::Last => Ok(context.last_line && (context.last_file || context.separate)),
|
Address::Last => Ok(reader.last_line()? && (context.last_file || context.separate)),
|
||||||
|
|
||||||
_ => panic!("invalid address type in match_address"),
|
_ => panic!("invalid address type in match_address"),
|
||||||
}
|
}
|
||||||
@@ -81,63 +71,66 @@ fn match_address(
|
|||||||
/// Return true if the command applies to the given pattern.
|
/// Return true if the command applies to the given pattern.
|
||||||
fn applies(
|
fn applies(
|
||||||
command: &mut Command,
|
command: &mut Command,
|
||||||
|
reader: &mut LineReader,
|
||||||
pattern: &mut IOChunk,
|
pattern: &mut IOChunk,
|
||||||
context: &mut ProcessingContext,
|
context: &mut ProcessingContext,
|
||||||
) -> UResult<bool> {
|
) -> UResult<bool> {
|
||||||
let linenum = context.line_number;
|
let linenum = context.line_number;
|
||||||
|
|
||||||
let result = if command.addr1.is_none() && command.addr2.is_none() {
|
let result = if command.addr1.is_none() && command.addr2.is_none() {
|
||||||
|
// No address
|
||||||
Ok(true)
|
Ok(true)
|
||||||
} else if let Some(addr2) = &command.addr2 {
|
} else if let Some(addr2) = &command.addr2 {
|
||||||
|
// Two addresses
|
||||||
if let Some(start) = command.start_line {
|
if let Some(start) = command.start_line {
|
||||||
match addr2.atype {
|
// Range is already latched active.
|
||||||
AddressType::RelLine => {
|
match addr2 {
|
||||||
if let AddressValue::LineNumber(n) = addr2.value {
|
Address::RelLine(n) => {
|
||||||
if linenum - start <= n {
|
if linenum - start > *n {
|
||||||
Ok(true)
|
command.start_line = None;
|
||||||
} else {
|
|
||||||
command.start_line = None;
|
|
||||||
Ok(false)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Ok(false)
|
Ok(false)
|
||||||
|
} else {
|
||||||
|
Ok(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
Address::Line(n) => {
|
||||||
if match_address(addr2, pattern, context, &command.location)? {
|
// Special case: already ended
|
||||||
|
if linenum > *n {
|
||||||
command.start_line = None;
|
command.start_line = None;
|
||||||
context.last_address = true;
|
Ok(false)
|
||||||
Ok(true)
|
|
||||||
} else if addr2.atype == AddressType::Line {
|
|
||||||
if let AddressValue::LineNumber(n) = addr2.value {
|
|
||||||
if linenum > n {
|
|
||||||
command.start_line = None;
|
|
||||||
Ok(false)
|
|
||||||
} else {
|
|
||||||
Ok(true)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Ok(true)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Address::StepMatch(step) => Ok((linenum - start).is_multiple_of(*step)),
|
||||||
|
Address::StepEnd(step) => {
|
||||||
|
// Inclusive end on multiple of step
|
||||||
|
if linenum.is_multiple_of(*step) {
|
||||||
|
command.start_line = None;
|
||||||
|
}
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
if match_address(addr2, reader, pattern, context, &command.location)? {
|
||||||
|
command.start_line = None;
|
||||||
|
context.last_address = true;
|
||||||
|
}
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if let Some(addr1) = &command.addr1 {
|
} else if let Some(addr1) = &command.addr1 {
|
||||||
if match_address(addr1, pattern, context, &command.location)? {
|
// See if latch must start.
|
||||||
match addr2.atype {
|
if match_address(addr1, reader, pattern, context, &command.location)? {
|
||||||
AddressType::Line => {
|
match addr2 {
|
||||||
if let AddressValue::LineNumber(n) = addr2.value {
|
Address::Line(n) => {
|
||||||
if linenum >= n {
|
if linenum >= *n {
|
||||||
context.last_address = true;
|
context.last_address = true;
|
||||||
} else {
|
} else {
|
||||||
command.start_line = Some(linenum);
|
command.start_line = Some(linenum);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AddressType::RelLine => {
|
Address::RelLine(n) => {
|
||||||
if let AddressValue::LineNumber(0) = addr2.value {
|
if *n == 0 {
|
||||||
context.last_address = true;
|
context.last_address = true;
|
||||||
} else {
|
} else {
|
||||||
command.start_line = Some(linenum);
|
command.start_line = Some(linenum);
|
||||||
@@ -155,9 +148,17 @@ fn applies(
|
|||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
} else if let Some(addr1) = &command.addr1 {
|
} else if let Some(addr1) = &command.addr1 {
|
||||||
Ok(match_address(addr1, pattern, context, &command.location)?)
|
// Single address
|
||||||
|
Ok(match_address(
|
||||||
|
addr1,
|
||||||
|
reader,
|
||||||
|
pattern,
|
||||||
|
context,
|
||||||
|
&command.location,
|
||||||
|
)?)
|
||||||
} else {
|
} else {
|
||||||
Ok(false)
|
// All allowed cases have been covered by the above logic.
|
||||||
|
panic!("impossible address combination");
|
||||||
};
|
};
|
||||||
|
|
||||||
if command.non_select {
|
if command.non_select {
|
||||||
@@ -431,8 +432,7 @@ fn process_file(
|
|||||||
context: &mut ProcessingContext,
|
context: &mut ProcessingContext,
|
||||||
) -> UResult<()> {
|
) -> UResult<()> {
|
||||||
// Loop over the input lines as pattern space.
|
// Loop over the input lines as pattern space.
|
||||||
'lines: while let Some((mut pattern, last_line)) = reader.get_line()? {
|
'lines: while let Some(mut pattern) = reader.get_line()? {
|
||||||
context.last_line = last_line;
|
|
||||||
context.line_number += 1;
|
context.line_number += 1;
|
||||||
context.substitution_made = false;
|
context.substitution_made = false;
|
||||||
// Set the script command from which to start.
|
// Set the script command from which to start.
|
||||||
@@ -455,7 +455,7 @@ fn process_file(
|
|||||||
while let Some(command_rc) = current.clone() {
|
while let Some(command_rc) = current.clone() {
|
||||||
let mut command = command_rc.borrow_mut();
|
let mut command = command_rc.borrow_mut();
|
||||||
|
|
||||||
if !applies(&mut command, &mut pattern, context)? {
|
if !applies(&mut command, reader, &mut pattern, context)? {
|
||||||
// Advance to next command
|
// Advance to next command
|
||||||
current = command.next.clone();
|
current = command.next.clone();
|
||||||
continue;
|
continue;
|
||||||
@@ -494,7 +494,7 @@ fn process_file(
|
|||||||
// At range end replace pattern space with text and
|
// At range end replace pattern space with text and
|
||||||
// start the next cycle.
|
// start the next cycle.
|
||||||
pattern.clear();
|
pattern.clear();
|
||||||
if command.addr2.is_none() || context.last_address || context.last_line {
|
if command.addr2.is_none() || context.last_address || reader.last_line()? {
|
||||||
let text = extract_variant!(command, Text);
|
let text = extract_variant!(command, Text);
|
||||||
output.write_str(text.as_ref())?;
|
output.write_str(text.as_ref())?;
|
||||||
}
|
}
|
||||||
@@ -567,6 +567,10 @@ fn process_file(
|
|||||||
'p' => {
|
'p' => {
|
||||||
// Write the pattern space to standard output.
|
// Write the pattern space to standard output.
|
||||||
write_chunk(output, context, &pattern)?;
|
write_chunk(output, context, &pattern)?;
|
||||||
|
if !pattern.is_newline_terminated() {
|
||||||
|
// !chomped equivalent
|
||||||
|
output.write_str("\n")?; // explicit \n
|
||||||
|
}
|
||||||
}
|
}
|
||||||
'P' => {
|
'P' => {
|
||||||
// Output pattern space, up to the first \n.
|
// Output pattern space, up to the first \n.
|
||||||
@@ -675,6 +679,21 @@ fn process_file(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Mark all address ranges non-active (and 0-starting ones as active).
|
||||||
|
fn reset_latched_address_ranges(range_commands: &mut [Rc<RefCell<Command>>]) {
|
||||||
|
for cmd_rc in range_commands.iter() {
|
||||||
|
let mut cmd = cmd_rc.borrow_mut();
|
||||||
|
|
||||||
|
cmd.start_line =
|
||||||
|
// Check for address-spec line 0 pre-latch extension.
|
||||||
|
if let Some(addr1) = &cmd.addr1 && matches!(addr1, Address::Line(0)) {
|
||||||
|
Some(0)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Process all input files
|
/// Process all input files
|
||||||
pub fn process_all_files(
|
pub fn process_all_files(
|
||||||
commands: Option<Rc<RefCell<Command>>>,
|
commands: Option<Rc<RefCell<Command>>>,
|
||||||
@@ -692,9 +711,11 @@ pub fn process_all_files(
|
|||||||
.map_err_context(|| format!("error opening input file {}", path.quote()))?;
|
.map_err_context(|| format!("error opening input file {}", path.quote()))?;
|
||||||
let output = in_place.begin(path)?;
|
let output = in_place.begin(path)?;
|
||||||
|
|
||||||
if context.separate {
|
if index == 0 || context.separate {
|
||||||
context.line_number = 0;
|
context.line_number = 0;
|
||||||
|
reset_latched_address_ranges(&mut context.range_commands);
|
||||||
}
|
}
|
||||||
|
|
||||||
context.input_name = path.quote().to_string();
|
context.input_name = path.quote().to_string();
|
||||||
process_file(&commands, &mut reader, output, context)?;
|
process_file(&commands, &mut reader, output, context)?;
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ use std::io::{Read, Write};
|
|||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use assert_fs::fixture::{FileWriteStr, PathChild};
|
use assert_fs::fixture::{FileWriteStr, PathChild};
|
||||||
|
|
||||||
|
use sha2::{Digest, Sha256};
|
||||||
use tempfile::NamedTempFile;
|
use tempfile::NamedTempFile;
|
||||||
use uutests::new_ucmd;
|
use uutests::new_ucmd;
|
||||||
|
|
||||||
@@ -225,6 +226,15 @@ check_output!(addr_escaped_delimiter, ["-n", "\\_l1\\_7_p", LINES1]);
|
|||||||
check_output!(addr_range_numeric, ["-n", "1,4p", LINES1]);
|
check_output!(addr_range_numeric, ["-n", "1,4p", LINES1]);
|
||||||
check_output!(addr_range_to_last, ["-n", "1,$p", LINES1, LINES2]);
|
check_output!(addr_range_to_last, ["-n", "1,$p", LINES1, LINES2]);
|
||||||
check_output!(addr_range_to_pattern, ["-n", "1,/l2_9/p", LINES1, LINES2]);
|
check_output!(addr_range_to_pattern, ["-n", "1,/l2_9/p", LINES1, LINES2]);
|
||||||
|
check_output!(
|
||||||
|
addr_range_straddle,
|
||||||
|
["-n", "/l1_3/,/l2_3/p", LINES1, LINES2]
|
||||||
|
);
|
||||||
|
check_output!(
|
||||||
|
addr_range_separate,
|
||||||
|
["-n", "--separate", "/l1_3/,/l2_3/p", LINES1, LINES2]
|
||||||
|
);
|
||||||
|
check_output!(addr_range_from_zero_to_pattern, ["-n", "0,/_1/p", LINES1]);
|
||||||
check_output!(addr_pattern_to_last, ["-n", "/4/,$p", LINES1, LINES2]);
|
check_output!(addr_pattern_to_last, ["-n", "/4/,$p", LINES1, LINES2]);
|
||||||
check_output!(addr_pattern_to_straddle, ["-n", "/4/,20p", LINES1, LINES2]);
|
check_output!(addr_pattern_to_straddle, ["-n", "/4/,20p", LINES1, LINES2]);
|
||||||
check_output!(addr_pattern_to_pattern, ["-n", "/4/,/10/p", LINES1, LINES2]);
|
check_output!(addr_pattern_to_pattern, ["-n", "/4/,/10/p", LINES1, LINES2]);
|
||||||
@@ -259,6 +269,10 @@ check_output!(
|
|||||||
);
|
);
|
||||||
check_output!(addr_empty_re_reuse, ["-n", "/_2/,//p", LINES1, LINES2]);
|
check_output!(addr_empty_re_reuse, ["-n", "/_2/,//p", LINES1, LINES2]);
|
||||||
check_output!(addr_simple_negation, ["-e", r"4,12!s/^/^/", LINES1]);
|
check_output!(addr_simple_negation, ["-e", r"4,12!s/^/^/", LINES1]);
|
||||||
|
check_output!(addr_range_even, ["-n", "0~2p", LINES1]);
|
||||||
|
check_output!(addr_range_odd, ["-n", "1~2p", LINES1]);
|
||||||
|
check_output!(addr_range_step_zero, ["-n", "10~0p", LINES1]);
|
||||||
|
check_output!(addr_range_end_multiple, ["-n", "/l1_2/,~10p", LINES1]);
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
// Substitution: s
|
// Substitution: s
|
||||||
@@ -987,6 +1001,42 @@ check_output!(pi, ["-f", "script/math.sed", "input/pi"]);
|
|||||||
// Solve the Towers of Hanoi puzzle
|
// Solve the Towers of Hanoi puzzle
|
||||||
check_output!(hanoi, ["-f", "script/hanoi.sed", "input/hanoi"]);
|
check_output!(hanoi, ["-f", "script/hanoi.sed", "input/hanoi"]);
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
// Long-running scripts
|
||||||
|
// Test with cargo test -- --ignored
|
||||||
|
|
||||||
|
// Check the output of Bach's prelude in C major from WTC book I.
|
||||||
|
// Run with cargo test test_bach_prelude_matches -- --ignored.
|
||||||
|
#[test]
|
||||||
|
#[ignore] // Slow; produces 5.8 MB of raw audio.
|
||||||
|
fn test_bach_prelude_matches() {
|
||||||
|
let res = new_ucmd!()
|
||||||
|
.args(&["-E", "-f", "script/bach.sed"])
|
||||||
|
.pipe_in("\n")
|
||||||
|
.succeeds();
|
||||||
|
|
||||||
|
// Compare SHA-256 output against GNU sed output.
|
||||||
|
let mut hasher = Sha256::new();
|
||||||
|
hasher.update(res.stdout());
|
||||||
|
let digest = hasher.finalize();
|
||||||
|
|
||||||
|
let got = hex::encode(digest);
|
||||||
|
assert_eq!(
|
||||||
|
got,
|
||||||
|
"c4e50d6791a60692745e958dc48d43a40bccea2ce5cea31b7125a40604cc3219"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw the Mandelbrot set.
|
||||||
|
#[ignore] // Slow; takes > 15" on an i7 CPU
|
||||||
|
#[test]
|
||||||
|
fn test_mandelbrod() {
|
||||||
|
new_ucmd!()
|
||||||
|
.args(&["-En", "-f", "script/mandelbrot.sed", "input/newline"])
|
||||||
|
.succeeds()
|
||||||
|
.stdout_is_fixture("output/mandelbrot");
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
// Error handling
|
// Error handling
|
||||||
#[test]
|
#[test]
|
||||||
@@ -1016,6 +1066,51 @@ fn test_undefined_label() {
|
|||||||
.stderr_is("sed: <script argument 1>:1:1: error: undefined label `foo'\n");
|
.stderr_is("sed: <script argument 1>:1:1: error: undefined label `foo'\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_addr0_non_posix() {
|
||||||
|
new_ucmd!()
|
||||||
|
.args(&["--posix", "0,/foo/p"])
|
||||||
|
.fails()
|
||||||
|
.code_is(1)
|
||||||
|
.stderr_is("sed: <script argument 1>:1:2: error: address 0 is invalid in POSIX mode\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_addr0_second_required() {
|
||||||
|
new_ucmd!()
|
||||||
|
.args(&["0p"])
|
||||||
|
.fails()
|
||||||
|
.code_is(1)
|
||||||
|
.stderr_is("sed: <script argument 1>:1:2: error: address 0 requires a second address\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_addr0_second_re_only() {
|
||||||
|
new_ucmd!()
|
||||||
|
.args(&["0,4p"])
|
||||||
|
.fails()
|
||||||
|
.code_is(1)
|
||||||
|
.stderr_is("sed: <script argument 1>:1:4: error: address 0 can only be used with a regular expression or ~step\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_step_match_non_posix() {
|
||||||
|
new_ucmd!()
|
||||||
|
.args(&["--posix", "3~2p"])
|
||||||
|
.fails()
|
||||||
|
.code_is(1)
|
||||||
|
.stderr_is("sed: <script argument 1>:1:3: error: ~step is invalid in POSIX mode\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_step_end_non_posix() {
|
||||||
|
new_ucmd!()
|
||||||
|
.args(&["--posix", "3,~2p"])
|
||||||
|
.fails()
|
||||||
|
.code_is(1)
|
||||||
|
.stderr_is("sed: <script argument 1>:1:4: error: ~step is invalid in POSIX mode\n");
|
||||||
|
}
|
||||||
|
|
||||||
// The following test diverse ways in which regexes are matched.
|
// The following test diverse ways in which regexes are matched.
|
||||||
// Search for 'regex\.' to find them in the code.
|
// Search for 'regex\.' to find them in the code.
|
||||||
#[test]
|
#[test]
|
||||||
@@ -1080,3 +1175,14 @@ fn test_missing_address_re() {
|
|||||||
.code_is(2)
|
.code_is(2)
|
||||||
.stderr_is("sed: <script argument 1>:2:3: 'input/lines1':1 error: no previous regular expression\n");
|
.stderr_is("sed: <script argument 1>:2:3: 'input/lines1':1 error: no previous regular expression\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
// Test for issue #143: Missing newline in output with `-e p`
|
||||||
|
#[test]
|
||||||
|
fn test_print_command_adds_newline() {
|
||||||
|
new_ucmd!()
|
||||||
|
.args(&["-e", "p"])
|
||||||
|
.pipe_in("foo")
|
||||||
|
.succeeds()
|
||||||
|
.stdout_is("foo\nfoo");
|
||||||
|
}
|
||||||
|
|||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
l1_2
|
||||||
|
l1_3
|
||||||
|
l1_4
|
||||||
|
l1_5
|
||||||
|
l1_6
|
||||||
|
l1_7
|
||||||
|
l1_8
|
||||||
|
l1_9
|
||||||
|
l1_10
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
l1_2
|
||||||
|
l1_4
|
||||||
|
l1_6
|
||||||
|
l1_8
|
||||||
|
l1_10
|
||||||
|
l1_12
|
||||||
|
l1_14
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
l1_1
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user