Skip to content

Commit ea7f4d7

Browse files
committed
Add glob matching
1 parent cd45496 commit ea7f4d7

6 files changed

Lines changed: 284 additions & 7 deletions

File tree

Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ clap = { version = "4.4", features = ["derive"] }
2525
flate2 = "1.0" # zlib compression for DAT2 format
2626
rayon = "1.8" # Parallel processing for faster extraction
2727

28+
# Cross-platform path handling
29+
glob = "0.3" # Glob pattern matching for cross-platform support
30+
2831
# Optional: Use mimalloc on Linux for better performance
2932
[target.'cfg(target_os = "linux")'.dependencies]
3033
mimalloc = "0.1"

src/common.rs

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ which DAT format it's working with.
88

99
// Import the libraries we need
1010
use anyhow::{bail, Context, Result}; // For error handling
11+
use glob::glob; // Cross-platform glob expansion
1112
use std::fs; // File system operations
1213
use std::path::{Path, PathBuf}; // Cross-platform path handling
1314

@@ -423,16 +424,17 @@ pub mod utils {
423424
.collect()
424425
}
425426

426-
/// Expand @response-file syntax into actual file list
427+
/// Expand @response-file syntax and glob patterns into actual file list
427428
///
428429
/// If files contains exactly one item starting with '@', reads that file
429-
/// and returns its lines as the file list. Otherwise returns files as-is.
430+
/// and returns its lines as the file list. Otherwise, expands any glob
431+
/// patterns and returns the expanded file list.
430432
///
431433
/// # Arguments
432-
/// * `files` - Command line file arguments (may contain @response-file)
434+
/// * `files` - Command line file arguments (may contain @response-file or globs)
433435
///
434436
/// # Returns
435-
/// * Expanded file list or error if response file cannot be read
437+
/// * Expanded file list or error if response file cannot be read or pattern fails
436438
pub fn expand_response_files(files: &[String]) -> Result<Vec<String>> {
437439
// Check if we have exactly one argument starting with '@'
438440
if files.len() == 1 && files[0].starts_with('@') {
@@ -453,8 +455,39 @@ pub mod utils {
453455
// Mixed usage - response file with other arguments
454456
bail!("Cannot mix @response-file with explicit file arguments");
455457
} else {
456-
// No response file, return as-is
457-
Ok(files.to_vec())
458+
// Expand glob patterns for each file argument
459+
let mut expanded_files = Vec::new();
460+
461+
for file in files {
462+
// Check if the pattern contains glob metacharacters
463+
if file.contains('*') || file.contains('?') || file.contains('[') {
464+
// Use glob to expand the pattern
465+
let mut found_matches = false;
466+
for entry in
467+
glob(file).with_context(|| format!("Invalid glob pattern: {file}"))?
468+
{
469+
match entry {
470+
Ok(path) => {
471+
expanded_files.push(path.to_string_lossy().into_owned());
472+
found_matches = true;
473+
}
474+
Err(e) => {
475+
bail!("Error expanding glob pattern '{file}': {e}");
476+
}
477+
}
478+
}
479+
480+
// If no matches found, this is an error (pattern doesn't exist)
481+
if !found_matches {
482+
bail!("Path does not exist: {file}");
483+
}
484+
} else {
485+
// Not a glob pattern, add as-is
486+
expanded_files.push(file.clone());
487+
}
488+
}
489+
490+
Ok(expanded_files)
458491
}
459492
}
460493

test.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,6 @@ fi
2626

2727
# Path consistency test
2828
./path_consistency.sh
29+
30+
# Glob pattern handling test
31+
./glob_handling.sh

tests/glob_handling.sh

Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
#!/bin/bash
2+
3+
set -xeu -o pipefail
4+
5+
# shellcheck source=tests/common.sh
6+
source "$(dirname "$0")/common.sh"
7+
8+
TEST_DIR="test_glob_handling"
9+
10+
# Clean up any previous test
11+
rm -rf "$TEST_DIR"
12+
mkdir -p "$TEST_DIR"
13+
cd "$TEST_DIR"
14+
15+
echo "=== Testing glob pattern expansion ==="
16+
17+
# Create test directory structure with various file types
18+
mkdir -p patch000/xxx patch000/yyy
19+
echo "content1" >patch000/1.txt
20+
echo "content2" >patch000/2.txt
21+
echo "content3" >patch000/xxx/3.txt
22+
echo "binary_data" >patch000/data.bin
23+
echo "another_file" >patch000/test.dat
24+
echo "nested_file" >patch000/yyy/nested.txt
25+
26+
echo "Directory structure:"
27+
find . -type f | sort
28+
29+
# Get the Windows binary path for all tests
30+
WIN_BINARY="$SCRIPT_DIR/../target/x86_64-pc-windows-gnu/release/dat3.exe"
31+
WINE_DAT3="$(winepath -w "$WIN_BINARY")"
32+
33+
echo ""
34+
echo "=== Test 1: Basic glob pattern ==="
35+
36+
# Test Linux build
37+
echo "Testing Linux: dat3 a test1_linux.dat 'patch000/*.txt'"
38+
"$DAT3" a test1_linux.dat 'patch000/*.txt'
39+
echo "Linux archive contents:"
40+
"$DAT3" l test1_linux.dat
41+
42+
# Verify Linux glob expansion
43+
echo "Verifying Linux glob expansion..."
44+
if ! "$DAT3" l test1_linux.dat patch000/1.txt >/dev/null 2>&1; then
45+
echo "ERROR: Linux - patch000/1.txt not found in archive"
46+
exit 1
47+
fi
48+
if ! "$DAT3" l test1_linux.dat patch000/2.txt >/dev/null 2>&1; then
49+
echo "ERROR: Linux - patch000/2.txt not found in archive"
50+
exit 1
51+
fi
52+
# Should NOT contain xxx/3.txt since it's in a subdirectory
53+
if "$DAT3" l test1_linux.dat patch000/xxx/3.txt >/dev/null 2>&1; then
54+
echo "ERROR: Linux - patch000/xxx/3.txt should not be in archive (subdirectory)"
55+
exit 1
56+
fi
57+
echo "Linux basic glob pattern verification passed!"
58+
59+
# Test Windows build
60+
echo "Testing Windows: wine cmd /c \"$WINE_DAT3 a test1_windows.dat patch000\\*.txt\""
61+
wine cmd /c "$WINE_DAT3 a test1_windows.dat patch000\\*.txt"
62+
echo "Windows archive contents:"
63+
wine cmd /c "$WINE_DAT3 l test1_windows.dat"
64+
65+
# Verify Windows glob expansion
66+
echo "Verifying Windows glob expansion..."
67+
if ! wine cmd /c "$WINE_DAT3 l test1_windows.dat patch000\\1.txt" >/dev/null 2>&1; then
68+
echo "ERROR: Windows - glob expansion failed"
69+
exit 1
70+
fi
71+
if ! wine cmd /c "$WINE_DAT3 l test1_windows.dat patch000\\2.txt" >/dev/null 2>&1; then
72+
printf "ERROR: Windows - patch000\\2.txt not found in archive\n"
73+
exit 1
74+
fi
75+
echo "Windows basic glob pattern verification passed!"
76+
77+
echo ""
78+
echo "=== Test 2: Recursive glob pattern ==="
79+
80+
# Test Linux build
81+
echo "Testing Linux recursive glob: patch000/**/*.txt"
82+
"$DAT3" a test2_linux.dat 'patch000/**/*.txt'
83+
echo "Linux recursive glob archive contents:"
84+
"$DAT3" l test2_linux.dat
85+
86+
# Verify Linux recursive pattern includes subdirectory files
87+
if ! "$DAT3" l test2_linux.dat patch000/xxx/3.txt >/dev/null 2>&1; then
88+
echo "ERROR: Linux - Recursive glob should include patch000/xxx/3.txt"
89+
exit 1
90+
fi
91+
if ! "$DAT3" l test2_linux.dat patch000/yyy/nested.txt >/dev/null 2>&1; then
92+
echo "ERROR: Linux - Recursive glob should include patch000/yyy/nested.txt"
93+
exit 1
94+
fi
95+
echo "Linux recursive glob pattern verification passed!"
96+
97+
# Test Windows build
98+
echo "Testing Windows recursive glob: patch000\\**\\*.txt"
99+
wine cmd /c "$WINE_DAT3 a test2_windows.dat patch000\\**\\*.txt"
100+
echo "Windows recursive glob archive contents:"
101+
wine cmd /c "$WINE_DAT3 l test2_windows.dat"
102+
103+
# Verify Windows recursive pattern
104+
if ! wine cmd /c "$WINE_DAT3 l test2_windows.dat patch000\\xxx\\3.txt" >/dev/null 2>&1; then
105+
printf "ERROR: Windows - Recursive glob should include patch000\\xxx\\3.txt\n"
106+
exit 1
107+
fi
108+
echo "Windows recursive glob pattern verification passed!"
109+
110+
echo ""
111+
echo "=== Test 3: Character range glob pattern ==="
112+
113+
# Test Linux build
114+
echo "Testing Linux character range glob: patch000/[12].txt"
115+
"$DAT3" a test3_linux.dat 'patch000/[12].txt'
116+
echo "Linux character range glob archive contents:"
117+
"$DAT3" l test3_linux.dat
118+
119+
# Verify Linux character range pattern
120+
if ! "$DAT3" l test3_linux.dat patch000/1.txt >/dev/null 2>&1; then
121+
echo "ERROR: Linux - Character range glob should include patch000/1.txt"
122+
exit 1
123+
fi
124+
if ! "$DAT3" l test3_linux.dat patch000/2.txt >/dev/null 2>&1; then
125+
echo "ERROR: Linux - Character range glob should include patch000/2.txt"
126+
exit 1
127+
fi
128+
echo "Linux character range glob pattern verification passed!"
129+
130+
# Test Windows build
131+
echo "Testing Windows character range glob: patch000\\[12].txt"
132+
wine cmd /c "$WINE_DAT3 a test3_windows.dat patch000\\[12].txt"
133+
echo "Windows character range glob archive contents:"
134+
wine cmd /c "$WINE_DAT3 l test3_windows.dat"
135+
136+
# Verify Windows character range pattern
137+
if ! wine cmd /c "$WINE_DAT3 l test3_windows.dat patch000\\1.txt" >/dev/null 2>&1; then
138+
printf "ERROR: Windows - Character range glob should include patch000\\1.txt\n"
139+
exit 1
140+
fi
141+
echo "Windows character range glob pattern verification passed!"
142+
143+
echo ""
144+
echo "=== Test 4: Question mark glob pattern ==="
145+
146+
# Test Linux build
147+
echo "Testing Linux question mark glob: patch000/?.txt"
148+
"$DAT3" a test4_linux.dat 'patch000/?.txt'
149+
echo "Linux question mark glob archive contents:"
150+
"$DAT3" l test4_linux.dat
151+
152+
# Verify Linux question mark pattern matches single characters
153+
if ! "$DAT3" l test4_linux.dat patch000/1.txt >/dev/null 2>&1; then
154+
echo "ERROR: Linux - Question mark glob should include patch000/1.txt"
155+
exit 1
156+
fi
157+
if ! "$DAT3" l test4_linux.dat patch000/2.txt >/dev/null 2>&1; then
158+
echo "ERROR: Linux - Question mark glob should include patch000/2.txt"
159+
exit 1
160+
fi
161+
echo "Linux question mark glob pattern verification passed!"
162+
163+
# Test Windows build
164+
echo "Testing Windows question mark glob: patch000\\?.txt"
165+
wine cmd /c "$WINE_DAT3 a test4_windows.dat patch000\\?.txt"
166+
echo "Windows question mark glob archive contents:"
167+
wine cmd /c "$WINE_DAT3 l test4_windows.dat"
168+
169+
# Verify Windows question mark pattern
170+
if ! wine cmd /c "$WINE_DAT3 l test4_windows.dat patch000\\1.txt" >/dev/null 2>&1; then
171+
printf "ERROR: Windows - Question mark glob should include patch000\\1.txt\n"
172+
exit 1
173+
fi
174+
echo "Windows question mark glob pattern verification passed!"
175+
176+
echo ""
177+
echo "=== Test 5: Mixed file type glob patterns ==="
178+
179+
# Test Linux build
180+
echo "Testing Linux mixed file types: patch000/*.txt patch000/*.dat patch000/*.bin"
181+
"$DAT3" a test5_linux.dat 'patch000/*.txt' 'patch000/*.dat' 'patch000/*.bin'
182+
echo "Linux mixed file type glob archive contents:"
183+
"$DAT3" l test5_linux.dat
184+
185+
# Verify Linux mixed file types
186+
if ! "$DAT3" l test5_linux.dat patch000/1.txt >/dev/null 2>&1; then
187+
echo "ERROR: Linux - Mixed types should include patch000/1.txt"
188+
exit 1
189+
fi
190+
if ! "$DAT3" l test5_linux.dat patch000/2.txt >/dev/null 2>&1; then
191+
echo "ERROR: Linux - Mixed types should include patch000/2.txt"
192+
exit 1
193+
fi
194+
if ! "$DAT3" l test5_linux.dat patch000/data.bin >/dev/null 2>&1; then
195+
echo "ERROR: Linux - Mixed types should include patch000/data.bin"
196+
exit 1
197+
fi
198+
if ! "$DAT3" l test5_linux.dat patch000/test.dat >/dev/null 2>&1; then
199+
echo "ERROR: Linux - Mixed types should include patch000/test.dat"
200+
exit 1
201+
fi
202+
echo "Linux mixed file type glob pattern verification passed!"
203+
204+
# Test Windows build
205+
echo "Testing Windows mixed file types: patch000\\*.txt patch000\\*.dat patch000\\*.bin"
206+
wine cmd /c "$WINE_DAT3 a test5_windows.dat patch000\\*.txt patch000\\*.dat patch000\\*.bin"
207+
echo "Windows mixed file type glob archive contents:"
208+
wine cmd /c "$WINE_DAT3 l test5_windows.dat"
209+
210+
# Verify Windows mixed file types
211+
if ! wine cmd /c "$WINE_DAT3 l test5_windows.dat patch000\\1.txt" >/dev/null 2>&1; then
212+
printf "ERROR: Windows - Mixed types should include patch000\\1.txt\n"
213+
exit 1
214+
fi
215+
if ! wine cmd /c "$WINE_DAT3 l test5_windows.dat patch000\\2.txt" >/dev/null 2>&1; then
216+
printf "ERROR: Windows - Mixed types should include patch000\\2.txt\n"
217+
exit 1
218+
fi
219+
if ! wine cmd /c "$WINE_DAT3 l test5_windows.dat patch000\\data.bin" >/dev/null 2>&1; then
220+
printf "ERROR: Windows - Mixed types should include patch000\\data.bin\n"
221+
exit 1
222+
fi
223+
if ! wine cmd /c "$WINE_DAT3 l test5_windows.dat patch000\\test.dat" >/dev/null 2>&1; then
224+
printf "ERROR: Windows - Mixed types should include patch000\\test.dat\n"
225+
exit 1
226+
fi
227+
echo "Windows mixed file type glob pattern verification passed!"
228+
229+
echo ""
230+
echo "All glob tests completed successfully!"
231+
echo "Both Linux and Windows builds passed all glob pattern tests!"

tests/path_consistency.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ rm -rf "$TEST_DIR"
1212
mkdir -p "$TEST_DIR"
1313
cd "$TEST_DIR"
1414

15-
echo "Testing path consistency with wildcard expansion..."
15+
echo "Testing path consistency with glob expansion..."
1616

1717
# Create test directory structure like the user's case
1818
mkdir -p patch000/xxx

0 commit comments

Comments
 (0)