Skip to content

Commit 893c1b9

Browse files
committed
Add a function to read the data of a single file in the tarball.
1 parent 37766a2 commit 893c1b9

File tree

4 files changed

+72
-1
lines changed

4 files changed

+72
-1
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "Tar"
22
uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e"
33
authors = ["Stefan Karpinski <[email protected]>"]
4-
version = "1.9.0"
4+
version = "1.10.0"
55

66
[deps]
77
ArgTools = "0dad84c5-d112-42e6-8d28-ef12dabb789f"

README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,19 @@ will also not be copied and will instead be skipped. By default, `extract` will
9898
detect whether symlinks can be created in `dir` or not and will automatically
9999
copy symlinks if they cannot be created.
100100

101+
### `Tar.extract_file`
102+
103+
```jl
104+
extract_file(tarball, filepath) -> Vector{UInt8}
105+
```
106+
* `tarball :: Union{AbstractString, AbstractCmd, IO}`
107+
* `filepath :: AbstractString`
108+
109+
Read the content of a single file inside the tarball archive.
110+
Return a `Vector{UInt8}` with the data, or `nothing` if no
111+
matching file was found. `filepath` should be specified as a path
112+
relative the tarball root.
113+
101114
### Tar.list
102115

103116
```jl

src/extract.jl

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -590,3 +590,36 @@ function read_data(
590590
r < n && error("premature end of tar file")
591591
return view(buf, 1:size)
592592
end
593+
594+
595+
"""
596+
extract_file(tarball::Union{AbstractString, IO, Cmd}, file::AbstractString) -> Vector{UInt8}
597+
598+
Extract the content of a single file from the tarball.
599+
Return a `Vector{UInt8}` with the data, or `nothing` if no
600+
matching file was found. `file` should be specified as a path
601+
relative the tarball root.
602+
"""
603+
function extract_file(tarball::ArgRead, file::AbstractString)::Union{Nothing,Vector{UInt8}}
604+
arg_read(tarball) do tar
605+
# TODO: Correct to filter out "."?
606+
parts = filter!(x -> x != ".", split(file, '/'; keepempty=false))
607+
predicate = hdr -> begin
608+
hdr_parts = filter!(x -> x != ".", split(hdr.path, '/'; keepempty=false))
609+
hdr.type == :file && parts == hdr_parts
610+
end
611+
buf = IOBuffer()
612+
found = false
613+
Tar.read_tarball(predicate, tar) do hdr, _
614+
found && throw(ArgumentError("multiple files in the tarball matches the filename $file"))
615+
found = true
616+
Tar.read_data(tar, buf, size=hdr.size)
617+
end
618+
if found
619+
return take!(buf)
620+
else
621+
# TODO: Better to throw instead?
622+
return nothing
623+
end
624+
end
625+
end

test/runtests.jl

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -601,6 +601,31 @@ end
601601
end
602602
end
603603

604+
@testset "API: extract_file" begin
605+
mktempdir() do dir
606+
open(joinpath(dir, "file.txt"), "w") do io
607+
write(io, "file at the root")
608+
end
609+
dir2 = mkdir(joinpath(dir, "directory"))
610+
open(joinpath(dir2, "file2.txt"), "w") do io
611+
write(io, "file in directory")
612+
end
613+
tarball = Tar.create(dir)
614+
for tar in (()->tarball, ()->open(tarball))
615+
bytes = Tar.extract_file(tar(), "file.txt")
616+
@test String(bytes) == "file at the root"
617+
bytes = Tar.extract_file(tar(), "./file.txt")
618+
@test String(bytes) == "file at the root"
619+
bytes = Tar.extract_file(tar(), "directory/file2.txt")
620+
@test String(bytes) == "file in directory"
621+
bytes = Tar.extract_file(tar(), "./directory/file2.txt")
622+
@test String(bytes) == "file in directory"
623+
bytes = Tar.extract_file(tar(), "non-existent")
624+
@test bytes === nothing
625+
end
626+
end
627+
end
628+
604629
@testset "API: rewrite" begin
605630
# reference standard tarball
606631
reference, hash₁ = make_test_tarball()

0 commit comments

Comments
 (0)