first commit

This commit is contained in:
niamtokik
2021-03-29 20:04:07 +00:00
commit a93a377155
7 changed files with 314 additions and 0 deletions

4
.formatter.exs Normal file
View File

@@ -0,0 +1,4 @@
# Used by "mix format"
[
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
]

27
.gitignore vendored Normal file
View File

@@ -0,0 +1,27 @@
# The directory Mix will write compiled artifacts to.
/_build/
# If you run "mix test --cover", coverage assets end up here.
/cover/
# The directory Mix downloads your dependencies sources to.
/deps/
# Where third-party dependencies like ExDoc output generated docs.
/doc/
# Ignore .fetch files in case you like to edit your project deps locally.
/.fetch
# If the VM crashes, it generates a dump, let's ignore it too.
erl_crash.dump
# Also ignore archive artifacts (built via "mix archive.build").
*.ez
# Ignore package tarball (built via "mix hex.build").
dotzip-*.tar
# Temporary files for e.g. tests
/tmp

19
README.md Normal file
View File

@@ -0,0 +1,19 @@
# Dotzip
## Installation
If [available in Hex](https://hex.pm/docs/publish), the package can be installed
by adding `dotzip` to your list of dependencies in `mix.exs`:
```elixir
def deps do
[
{:dotzip, "~> 0.1.0"}
]
end
```
Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)
and published on [HexDocs](https://hexdocs.pm). Once published, the docs can
be found at [https://hexdocs.pm/dotzip](https://hexdocs.pm/dotzip).

227
lib/dotzip.ex Normal file
View File

@@ -0,0 +1,227 @@
defmodule Dotzip do
@moduledoc """
Elixir Implementation of ZIP File Format. https://pkware.cachefly.net/webdocs/APPNOTE/APPNOTE-6.3.3.TXT
"""
def decode(file) do
{:ok, local, rest} = Dotzip.LocalFileHeader.decode(file)
{:ok, central, r} = Dotzip.CentralDirectoryHeader.decode(rest)
{:ok, e, rr} = Dotzip.EndCentralDirectory.decode(r)
{local, central, e, rr}
end
end
defmodule Dotzip.LocalFileHeader do
defp signature(<< 0x50, 0x4b, 0x03, 0x04, rest::bitstring >>) do
{:ok, %{}, rest}
end
defp version({:ok, data, << version::binary-size(2), rest::bitstring >>}) do
{:ok, Map.put(data, :version, version), rest}
end
defp purpose_flag({:ok, data, << purpose_flag::binary-size(2), rest::bitstring >>}) do
{:ok, Map.put(data, :purpose_flag, purpose_flag), rest}
end
defp compression_method({:ok, data, << compression_method::binary-size(2), rest::bitstring >>}) do
{:ok, Map.put(data, :compression_method, compression_method), rest}
end
defp last_modification_time({:ok, data, << last_modification::little-size(16), rest::bitstring >>}) do
{:ok, Map.put(data, :last_modification_time, last_modification), rest}
end
defp last_modification_date({:ok, data, << last_modification_date::little-size(16), rest::bitstring >>}) do
{:ok, Map.put(data, :last_modification_date, last_modification_date), rest}
end
defp crc32({:ok, data, << crc32::binary-size(4), rest::bitstring >>}) do
{:ok, Map.put(data, :crc32, crc32), rest}
end
defp compressed_size({:ok, data, << compressed_size::little-size(32), rest::bitstring >>}) do
{:ok, Map.put(data, :compressed_size, compressed_size), rest}
end
defp uncompressed_size({:ok, data, << uncompressed_size::little-size(32), rest::bitstring >>}) do
{:ok, Map.put(data, :uncompressed_size, uncompressed_size), rest}
end
defp file_name_length({:ok, data, << file_name_length::little-size(16), rest::bitstring >>}) do
{:ok, Map.put(data, :file_name_length, file_name_length), rest }
end
defp extra_field_length({:ok, data, << extra_field_length::little-size(16), rest::bitstring >>}) do
{:ok, Map.put(data, :extra_field_length, extra_field_length), rest}
end
defp file_name({:ok, data, rest}) do
%{ :file_name_length => file_name_length } = data
<<file_name::binary-size(file_name_length), r::bitstring>> = rest
{:ok, Map.put(data, :file_name, file_name), r}
end
defp extra_field({:ok, data, rest}) do
%{ :extra_field_length => extra_field_length } = data
<<extra_field::binary-size(extra_field_length), r::bitstring>> = rest
{:ok, Map.put(data, :extra_field, extra_field), r}
end
defp content({:ok, data, rest}) do
%{ :compressed_size => compressed_size } = data
<<content::binary-size(compressed_size), r::bitstring>> = rest
{:ok, Map.put(data, :content, content), r}
end
def decode(file) do
{:ok, local_file_header, rest} = signature(file)
|> version()
|> purpose_flag()
|> compression_method()
|> last_modification_time()
|> last_modification_date()
|> crc32()
|> compressed_size()
|> uncompressed_size()
|> file_name_length()
|> extra_field_length()
|> file_name()
|> extra_field()
|> content()
end
end
defmodule Dotzip.CentralDirectoryHeader do
defp signature(<< 0x50, 0x4b, 0x01, 0x02, rest::bitstring >>) do
{:ok, %{}, rest}
end
defp version_made({:ok, data, << version::binary-size(2), rest::bitstring >>}) do
{:ok, Map.put(data, :version_made, version), rest}
end
defp version_needed({:ok, data, << version::binary-size(2), rest::bitstring >>}) do
{:ok, Map.put(data, :version_needed, version), rest}
end
defp purpose_flag({:ok, data, << purpose_flag::binary-size(2), rest::bitstring >>}) do
{:ok, Map.put(data, :purpose_flag, purpose_flag), rest}
end
defp compression_method({:ok, data, << compression_method::binary-size(2), rest::bitstring >>}) do
{:ok, Map.put(data, :compression_method, compression_method), rest}
end
defp last_modification_time({:ok, data, << last_modification::little-size(16), rest::bitstring >>}) do
{:ok, Map.put(data, :last_modification_time, last_modification), rest}
end
defp last_modification_date({:ok, data, << last_modification_date::little-size(16), rest::bitstring >>}) do
{:ok, Map.put(data, :last_modification_date, last_modification_date), rest}
end
defp crc32({:ok, data, << crc32::binary-size(4), rest::bitstring >>}) do
{:ok, Map.put(data, :crc32, crc32), rest}
end
defp compressed_size({:ok, data, << compressed_size::little-size(32), rest::bitstring >>}) do
{:ok, Map.put(data, :compressed_size, compressed_size), rest}
end
defp uncompressed_size({:ok, data, << uncompressed_size::little-size(32), rest::bitstring >>}) do
{:ok, Map.put(data, :uncompressed_size, uncompressed_size), rest}
end
defp file_name_length({:ok, data, << file_name_length::little-size(16), rest::bitstring >>}) do
{:ok, Map.put(data, :file_name_length, file_name_length), rest }
end
defp extra_field_length({:ok, data, << extra_field_length::little-size(16), rest::bitstring >>}) do
{:ok, Map.put(data, :extra_field_length, extra_field_length), rest}
end
defp file_comment_length({:ok, data, << file_comment_length::little-size(16), rest::bitstring >>}) do
{:ok, Map.put(data, :file_comment_length, file_comment_length), rest}
end
defp disk_number_start({:ok, data, << disk_number_start::little-size(16), rest::bitstring >>}) do
{:ok, Map.put(data, :disk_number_start, disk_number_start), rest}
end
defp internal_file_attributes({:ok, data, << internal_file_attributes::binary-size(2), rest::bitstring >>}) do
{:ok, Map.put(data, :internal_file_attributes, internal_file_attributes), rest}
end
defp external_file_attributes({:ok, data, << external_file_attributes::binary-size(4), rest::bitstring >>}) do
{:ok, Map.put(data, :external_file_attributes, external_file_attributes), rest}
end
defp relative_offset({:ok, data, << relative_offset::little-size(32), rest::bitstring >>}) do
{:ok, Map.put(data, :relative_offset, relative_offset), rest}
end
defp file_name({:ok, data, rest}) do
%{ :file_name_length => file_name_length } = data
<<file_name::binary-size(file_name_length), r::bitstring>> = rest
{:ok, Map.put(data, :file_name, file_name), r}
end
defp extra_field({:ok, data, rest}) do
%{ :extra_field_length => extra_field_length } = data
<<extra_field::binary-size(extra_field_length), r::bitstring>> = rest
{:ok, Map.put(data, :extra_field, extra_field), r}
end
defp file_comment({:ok, data, rest}) do
%{ :file_comment_length => file_comment_length } = data
<<file_comment::binary-size(file_comment_length), r::bitstring>> = rest
{:ok, Map.put(data, :file_comment, file_comment), r}
end
def decode(file) do
{:ok, central_directory_header, rest} = signature(file)
|> version_made()
|> version_needed()
|> purpose_flag()
|> compression_method()
|> last_modification_time()
|> last_modification_date()
|> crc32()
|> compressed_size()
|> uncompressed_size()
|> file_name_length()
|> extra_field_length()
|> file_comment_length()
|> disk_number_start()
|> internal_file_attributes()
|> external_file_attributes()
|> relative_offset()
|> file_name()
|> extra_field()
|> file_comment()
end
end
defmodule Dotzip.EndCentralDirectory do
defp signature(<< 0x50, 0x4b, 0x05, 0x06, rest::bitstring >>) do
{:ok, %{}, rest}
end
defp number_disk({:ok, data, << number_disk::little-size(32), rest::bitstring >>}) do
{:ok, Map.put(data, :number_disk, number_disk), rest}
end
def decode(file) do
{:ok, end_central_directory, rest} = signature(file)
|> number_disk()
end
end

28
mix.exs Normal file
View File

@@ -0,0 +1,28 @@
defmodule Dotzip.MixProject do
use Mix.Project
def project do
[
app: :dotzip,
version: "0.1.0",
elixir: "~> 1.11",
start_permanent: Mix.env() == :prod,
deps: deps()
]
end
# Run "mix help compile.app" to learn about applications.
def application do
[
extra_applications: [:logger]
]
end
# Run "mix help deps" to learn about dependencies.
defp deps do
[
# {:dep_from_hexpm, "~> 0.3.0"},
# {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
]
end
end

8
test/dotzip_test.exs Normal file
View File

@@ -0,0 +1,8 @@
defmodule DotzipTest do
use ExUnit.Case
doctest Dotzip
test "local file header" do
assert :world == :world
end
end

1
test/test_helper.exs Normal file
View File

@@ -0,0 +1 @@
ExUnit.start()