zipfixx 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
zipfixx-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 SouzaF19
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
zipfixx-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,179 @@
1
+ Metadata-Version: 2.4
2
+ Name: zipfixx
3
+ Version: 0.1.0
4
+ Summary: CLI para corrigir e extrair arquivos ZIP com nomes Unicode no Windows.
5
+ Author: SouzaF19
6
+ License: MIT
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Operating System :: OS Independent
10
+ Requires-Python: >=3.9
11
+ Description-Content-Type: text/markdown
12
+ License-File: LICENSE
13
+ Dynamic: license-file
14
+
15
+ # ZipFix
16
+
17
+ **Um CLI para corrigir e extrair arquivos ZIP com nomes Unicode no Windows.**
18
+
19
+ [![CI](https://github.com/SouzaF19/zipfix/actions/workflows/ci.yml/badge.svg)](https://github.com/SouzaF19/zipfix/actions/workflows/ci.yml)
20
+ [![Release](https://img.shields.io/github/v/tag/SouzaF19/zipfix)](https://github.com/SouzaF19/zipfix/releases)
21
+ [![License](https://img.shields.io/badge/license-MIT-brightgreen)](https://github.com/SouzaF19/zipfix/blob/main/LICENSE)
22
+
23
+ ---
24
+
25
+ ## 🎯 Sobre o problema
26
+
27
+ Ao descompactar arquivos `.zip` no Windows, nomes de arquivos que contêm caracteres
28
+ especiais ou Unicode (acentuação, símbolos, emojis) podem chegar corrompidos — virando
29
+ `????`, `ã`, ou sequências ininteligíveis.
30
+
31
+ Isso acontece porque a especificação do formato ZIP exige que nomes não-ASCII sejam
32
+ marcados com o *flag* de UTF-8 (bit `0x800`) na entrada. Dependendo da ferramenta que
33
+ escreveu ou leu o ZIP, esse flag é ignorado ou aplicado de forma inconsistente, resultando
34
+ em perda de caracteres.
35
+
36
+ ### Por que isso importa
37
+
38
+ - Extrair um ZIP com `Compress-Archive` / `Expand-Archive` no PowerShell pode gerar nomes
39
+ quebrados;
40
+ - Prefixos e nomes com `ä`, `ç`, `ã`, `€`, emojis e combinações passam a ser ininteligíveis;
41
+ - Leitores em outras plataformas (Linux, Web, Android) reagem mal a nomes malformados.
42
+
43
+ ---
44
+
45
+ ## 🔧 A solução
46
+
47
+ O **ZipFix** é um pequeno CLI que:
48
+
49
+ 1. Lê um arquivo `.zip`;
50
+ 2. Valida e sanitiza cada entrada (remove caracteres proibidos e limita o tamanho);
51
+ 3. Reescreve o ZIP com nomes seguros e com encoding correto (UTF-8);
52
+ 4. Extrai os arquivos de forma determinística.
53
+
54
+ ---
55
+
56
+ ## ✨ Funcionalidades
57
+
58
+ - ✅ Correção de nomes em entradas de ZIP;
59
+ - ✅ Sanitização de caracteres inválidos no Windows;
60
+ - ✅ Limitação de tamanho de nome;
61
+ - ✅ Reescrita de ZIP com encoding correto;
62
+ - ✅ Extração com nomes sanitizados;
63
+ - ✅ Relatório de entradas reescritas/extraídas.
64
+
65
+ ---
66
+
67
+ ## 🧰 Instalação
68
+
69
+ ```bash
70
+ pip install zipfix
71
+ ```
72
+
73
+ ou, via ferramenta de empacotamento:
74
+
75
+ ```bash
76
+ uv tool install zipfix
77
+ ```
78
+
79
+ ---
80
+
81
+ ## 🚀 Uso
82
+
83
+ ```bash
84
+ # Corrigir (reescrever) um ZIP
85
+ zipflix fix entrada.zip --out saida_corrigida.zip
86
+
87
+ # Extrair um ZIP com nomes sanitizados
88
+ zipflix extract entrada.zip destino/
89
+ ```
90
+
91
+ ### Exemplo
92
+
93
+ ```bash
94
+ zipflix fix ./downloads/arquivo.zip --out ./downloads/arquivo_corrigido.zip
95
+ zipflix extract ./downloads/arquivo_corrigido.zip ./extraido/
96
+ ```
97
+
98
+ ---
99
+
100
+ ## 🧠 Como funciona
101
+
102
+ - Usa o módulo nativo de ZIP (stdlib) que trata UTF-8;
103
+ - Emite o novo arquivo com o flag UTF-8 nas entradas;
104
+ - Sanitiza nomes proibidos no Windows (`|`, `<`, `>`, `:`, `"`, `?`, `*`, `\`) e caracteres de controle.
105
+
106
+ ---
107
+
108
+ ## 🗂️ Estrutura do projeto
109
+
110
+ ```text
111
+ C:/zipflix
112
+ ├── zipflix/
113
+ │ ├── __init__.py
114
+ │ ├── cli.py
115
+ │ ├── fixer.py
116
+ │ └── __main__.py
117
+ ├── tests/
118
+ │ └── test_fixer.py
119
+ ├── .github/
120
+ │ └── workflows/
121
+ │ └── ci.yml
122
+ ├── Makefile
123
+ ├── pyproject.toml
124
+ ├── README.md
125
+ └── LICENSE
126
+ ```
127
+
128
+ ---
129
+
130
+ ## 🔭 Próximos passos
131
+
132
+ - Testes com ZIPs reais, incluindo nomes com emoji;
133
+ - Suporte a criptografia (AES).
134
+
135
+ ---
136
+
137
+ ## 📄 Licença
138
+
139
+ MIT
140
+
141
+ ---
142
+
143
+ ## 📦 WinPathFix
144
+
145
+ **Um CLI para renomear arquivos soltos com caracteres invalidos no Windows.**
146
+
147
+ - Detecta e renomeia arquivos com caracteres proibidos (`|`, `<`, `>`, `:`, `"`, `?`, `*`, `\`).
148
+ - Preserva Unicode e estrutura de pastas.
149
+ - Limita tamanho de nome a 255 caracteres por segmento.
150
+
151
+ ```bash
152
+ winpathfix fix ./pastainvalida/
153
+ ```
154
+
155
+ ---
156
+
157
+ ## 📦 PyPI
158
+
159
+ ```bash
160
+ pip install zipfix
161
+ ```
162
+
163
+ ou
164
+
165
+ ```bash
166
+ pip install git+https://github.com/SouzaF19/zipfix.git
167
+ ```
168
+
169
+ ---
170
+
171
+ ## 📄 Licença
172
+
173
+ MIT — ver [LICENSE](LICENSE).
174
+
175
+ ---
176
+
177
+ ## 📬 Contato
178
+
179
+ - GitHub: [@SouzaF19](https://github.com/SouzaF19)
@@ -0,0 +1,165 @@
1
+ # ZipFix
2
+
3
+ **Um CLI para corrigir e extrair arquivos ZIP com nomes Unicode no Windows.**
4
+
5
+ [![CI](https://github.com/SouzaF19/zipfix/actions/workflows/ci.yml/badge.svg)](https://github.com/SouzaF19/zipfix/actions/workflows/ci.yml)
6
+ [![Release](https://img.shields.io/github/v/tag/SouzaF19/zipfix)](https://github.com/SouzaF19/zipfix/releases)
7
+ [![License](https://img.shields.io/badge/license-MIT-brightgreen)](https://github.com/SouzaF19/zipfix/blob/main/LICENSE)
8
+
9
+ ---
10
+
11
+ ## 🎯 Sobre o problema
12
+
13
+ Ao descompactar arquivos `.zip` no Windows, nomes de arquivos que contêm caracteres
14
+ especiais ou Unicode (acentuação, símbolos, emojis) podem chegar corrompidos — virando
15
+ `????`, `ã`, ou sequências ininteligíveis.
16
+
17
+ Isso acontece porque a especificação do formato ZIP exige que nomes não-ASCII sejam
18
+ marcados com o *flag* de UTF-8 (bit `0x800`) na entrada. Dependendo da ferramenta que
19
+ escreveu ou leu o ZIP, esse flag é ignorado ou aplicado de forma inconsistente, resultando
20
+ em perda de caracteres.
21
+
22
+ ### Por que isso importa
23
+
24
+ - Extrair um ZIP com `Compress-Archive` / `Expand-Archive` no PowerShell pode gerar nomes
25
+ quebrados;
26
+ - Prefixos e nomes com `ä`, `ç`, `ã`, `€`, emojis e combinações passam a ser ininteligíveis;
27
+ - Leitores em outras plataformas (Linux, Web, Android) reagem mal a nomes malformados.
28
+
29
+ ---
30
+
31
+ ## 🔧 A solução
32
+
33
+ O **ZipFix** é um pequeno CLI que:
34
+
35
+ 1. Lê um arquivo `.zip`;
36
+ 2. Valida e sanitiza cada entrada (remove caracteres proibidos e limita o tamanho);
37
+ 3. Reescreve o ZIP com nomes seguros e com encoding correto (UTF-8);
38
+ 4. Extrai os arquivos de forma determinística.
39
+
40
+ ---
41
+
42
+ ## ✨ Funcionalidades
43
+
44
+ - ✅ Correção de nomes em entradas de ZIP;
45
+ - ✅ Sanitização de caracteres inválidos no Windows;
46
+ - ✅ Limitação de tamanho de nome;
47
+ - ✅ Reescrita de ZIP com encoding correto;
48
+ - ✅ Extração com nomes sanitizados;
49
+ - ✅ Relatório de entradas reescritas/extraídas.
50
+
51
+ ---
52
+
53
+ ## 🧰 Instalação
54
+
55
+ ```bash
56
+ pip install zipfix
57
+ ```
58
+
59
+ ou, via ferramenta de empacotamento:
60
+
61
+ ```bash
62
+ uv tool install zipfix
63
+ ```
64
+
65
+ ---
66
+
67
+ ## 🚀 Uso
68
+
69
+ ```bash
70
+ # Corrigir (reescrever) um ZIP
71
+ zipflix fix entrada.zip --out saida_corrigida.zip
72
+
73
+ # Extrair um ZIP com nomes sanitizados
74
+ zipflix extract entrada.zip destino/
75
+ ```
76
+
77
+ ### Exemplo
78
+
79
+ ```bash
80
+ zipflix fix ./downloads/arquivo.zip --out ./downloads/arquivo_corrigido.zip
81
+ zipflix extract ./downloads/arquivo_corrigido.zip ./extraido/
82
+ ```
83
+
84
+ ---
85
+
86
+ ## 🧠 Como funciona
87
+
88
+ - Usa o módulo nativo de ZIP (stdlib) que trata UTF-8;
89
+ - Emite o novo arquivo com o flag UTF-8 nas entradas;
90
+ - Sanitiza nomes proibidos no Windows (`|`, `<`, `>`, `:`, `"`, `?`, `*`, `\`) e caracteres de controle.
91
+
92
+ ---
93
+
94
+ ## 🗂️ Estrutura do projeto
95
+
96
+ ```text
97
+ C:/zipflix
98
+ ├── zipflix/
99
+ │ ├── __init__.py
100
+ │ ├── cli.py
101
+ │ ├── fixer.py
102
+ │ └── __main__.py
103
+ ├── tests/
104
+ │ └── test_fixer.py
105
+ ├── .github/
106
+ │ └── workflows/
107
+ │ └── ci.yml
108
+ ├── Makefile
109
+ ├── pyproject.toml
110
+ ├── README.md
111
+ └── LICENSE
112
+ ```
113
+
114
+ ---
115
+
116
+ ## 🔭 Próximos passos
117
+
118
+ - Testes com ZIPs reais, incluindo nomes com emoji;
119
+ - Suporte a criptografia (AES).
120
+
121
+ ---
122
+
123
+ ## 📄 Licença
124
+
125
+ MIT
126
+
127
+ ---
128
+
129
+ ## 📦 WinPathFix
130
+
131
+ **Um CLI para renomear arquivos soltos com caracteres invalidos no Windows.**
132
+
133
+ - Detecta e renomeia arquivos com caracteres proibidos (`|`, `<`, `>`, `:`, `"`, `?`, `*`, `\`).
134
+ - Preserva Unicode e estrutura de pastas.
135
+ - Limita tamanho de nome a 255 caracteres por segmento.
136
+
137
+ ```bash
138
+ winpathfix fix ./pastainvalida/
139
+ ```
140
+
141
+ ---
142
+
143
+ ## 📦 PyPI
144
+
145
+ ```bash
146
+ pip install zipfix
147
+ ```
148
+
149
+ ou
150
+
151
+ ```bash
152
+ pip install git+https://github.com/SouzaF19/zipfix.git
153
+ ```
154
+
155
+ ---
156
+
157
+ ## 📄 Licença
158
+
159
+ MIT — ver [LICENSE](LICENSE).
160
+
161
+ ---
162
+
163
+ ## 📬 Contato
164
+
165
+ - GitHub: [@SouzaF19](https://github.com/SouzaF19)
@@ -0,0 +1,23 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "zipfixx"
7
+ version = "0.1.0"
8
+ description = "CLI para corrigir e extrair arquivos ZIP com nomes Unicode no Windows."
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "SouzaF19" }]
13
+ classifiers = [
14
+ "Programming Language :: Python :: 3",
15
+ "License :: OSI Approved :: MIT License",
16
+ "Operating System :: OS Independent",
17
+ ]
18
+
19
+ [project.scripts]
20
+ zipfix = "zipflix.cli:main"
21
+
22
+ [tool.setuptools]
23
+ packages = ["zipflix"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,74 @@
1
+ """Testes do ZipFix: sanitização e reescrita/extração de nomes em ZIP."""
2
+
3
+ import os
4
+ import tempfile
5
+ import unittest
6
+ import zipfile
7
+
8
+ from zipflix import fixer
9
+
10
+
11
+ class TestFixer(unittest.TestCase):
12
+ def setUp(self):
13
+ self.tmp = tempfile.TemporaryDirectory()
14
+ self.addCleanup(self.tmp.cleanup)
15
+ self.tmpdir = self.tmp.name
16
+
17
+ def _make_zip(self, path):
18
+ entries = {
19
+ "docs/relatorio-final.txt": b"conteudo final\n",
20
+ "docs/relatorio com acento-ãã.txt": b"acentos\n",
21
+ "emoji-🚀-teste.txt": b"emoji\n",
22
+ "invalid|name*.txt": b"invalido\n",
23
+ }
24
+ with zipfile.ZipFile(path, "w", zipfile.ZIP_DEFLATED) as zf:
25
+ for name, data in entries.items():
26
+ info = zipfile.ZipInfo(filename=name, date_time=(2026, 11, 7, 12, 0, 0))
27
+ info.compress_type = zipfile.ZIP_DEFLATED
28
+ zf.writestr(info, data)
29
+
30
+ def test_sanitize_name(self):
31
+ self.assertEqual(fixer.sanitize_name("docs/relatorio-ãã.txt"), "docs/relatorio-ãã.txt")
32
+ self.assertEqual(fixer.sanitize_name("invalid|name*.txt"), "invalidname.txt")
33
+ self.assertEqual(fixer.sanitize_name("bad:char*|name?.txt"), "badcharname.txt")
34
+
35
+ def test_sanitize_blocks_parent_traversal(self):
36
+ self.assertEqual(fixer.sanitize_name("../../evil.txt"), "evil.txt")
37
+ self.assertEqual(fixer.sanitize_name("../evil.txt"), "evil.txt")
38
+ self.assertNotIn("..", fixer.sanitize_name("../../evil.txt"))
39
+
40
+ def test_sanitize_truncates_per_segment(self):
41
+ long_name = "a" * 300
42
+ self.assertEqual(len(fixer.sanitize_name(long_name)) <= 255, True)
43
+
44
+ def test_fix_zip_rewrites_names(self):
45
+ src = os.path.join(self.tmpdir, "entrada.zip")
46
+ dst = os.path.join(self.tmpdir, "saida.zip")
47
+ self._make_zip(src)
48
+ results = fixer.fix_zip(src, dst)
49
+ self.assertTrue(len(results) > 0)
50
+ with zipfile.ZipFile(dst, "r") as zf:
51
+ names = zf.namelist()
52
+ self.assertIn("docs/relatorio-final.txt", names)
53
+ self.assertIn("docs/relatorio com acento-ãã.txt", names)
54
+ self.assertIn("emoji-🚀-teste.txt", names)
55
+ self.assertIn("invalidname.txt", names)
56
+ self.assertNotIn("invalid|name*.txt", names)
57
+
58
+ def test_fix_zip_same_path_raises(self):
59
+ src = os.path.join(self.tmpdir, "entrada.zip")
60
+ self._make_zip(src)
61
+ with self.assertRaises(ValueError):
62
+ fixer.fix_zip(src, src)
63
+
64
+ def test_extract_zip_sanitizes(self):
65
+ src = os.path.join(self.tmpdir, "entrada2.zip")
66
+ dest = os.path.join(self.tmpdir, "destino")
67
+ self._make_zip(src)
68
+ count = fixer.extract_zip(src, dest)
69
+ self.assertEqual(count, 4)
70
+ self.assertTrue(os.path.exists(os.path.join(dest, "emoji-🚀-teste.txt")))
71
+
72
+
73
+ if __name__ == "__main__":
74
+ unittest.main()
@@ -0,0 +1,26 @@
1
+ """Testes do WinPathFix: coleta de renomes em diretorio."""
2
+
3
+ import os
4
+ import tempfile
5
+ import unittest
6
+
7
+ from winpathfix import fixer
8
+ from winpathfix import cli
9
+
10
+
11
+ class TestWinPathFix(unittest.TestCase):
12
+ def setUp(self):
13
+ self.tmp = tempfile.TemporaryDirectory()
14
+ self.addCleanup(self.tmp.cleanup)
15
+ self.tmpdir = self.tmp.name
16
+
17
+ def test_collect_renames_empty_for_clean_dir(self):
18
+ open(os.path.join(self.tmpdir, "ok_renome.txt"), "w").write("x")
19
+ self.assertEqual(fixer.collect_renames(self.tmpdir), [])
20
+
21
+ def test_collect_renames_blocks_traversal(self):
22
+ self.assertEqual(fixer.sanitize_name("../../evil.txt"), "evil.txt")
23
+
24
+
25
+ if __name__ == "__main__":
26
+ unittest.main()
@@ -0,0 +1,179 @@
1
+ Metadata-Version: 2.4
2
+ Name: zipfixx
3
+ Version: 0.1.0
4
+ Summary: CLI para corrigir e extrair arquivos ZIP com nomes Unicode no Windows.
5
+ Author: SouzaF19
6
+ License: MIT
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Operating System :: OS Independent
10
+ Requires-Python: >=3.9
11
+ Description-Content-Type: text/markdown
12
+ License-File: LICENSE
13
+ Dynamic: license-file
14
+
15
+ # ZipFix
16
+
17
+ **Um CLI para corrigir e extrair arquivos ZIP com nomes Unicode no Windows.**
18
+
19
+ [![CI](https://github.com/SouzaF19/zipfix/actions/workflows/ci.yml/badge.svg)](https://github.com/SouzaF19/zipfix/actions/workflows/ci.yml)
20
+ [![Release](https://img.shields.io/github/v/tag/SouzaF19/zipfix)](https://github.com/SouzaF19/zipfix/releases)
21
+ [![License](https://img.shields.io/badge/license-MIT-brightgreen)](https://github.com/SouzaF19/zipfix/blob/main/LICENSE)
22
+
23
+ ---
24
+
25
+ ## 🎯 Sobre o problema
26
+
27
+ Ao descompactar arquivos `.zip` no Windows, nomes de arquivos que contêm caracteres
28
+ especiais ou Unicode (acentuação, símbolos, emojis) podem chegar corrompidos — virando
29
+ `????`, `ã`, ou sequências ininteligíveis.
30
+
31
+ Isso acontece porque a especificação do formato ZIP exige que nomes não-ASCII sejam
32
+ marcados com o *flag* de UTF-8 (bit `0x800`) na entrada. Dependendo da ferramenta que
33
+ escreveu ou leu o ZIP, esse flag é ignorado ou aplicado de forma inconsistente, resultando
34
+ em perda de caracteres.
35
+
36
+ ### Por que isso importa
37
+
38
+ - Extrair um ZIP com `Compress-Archive` / `Expand-Archive` no PowerShell pode gerar nomes
39
+ quebrados;
40
+ - Prefixos e nomes com `ä`, `ç`, `ã`, `€`, emojis e combinações passam a ser ininteligíveis;
41
+ - Leitores em outras plataformas (Linux, Web, Android) reagem mal a nomes malformados.
42
+
43
+ ---
44
+
45
+ ## 🔧 A solução
46
+
47
+ O **ZipFix** é um pequeno CLI que:
48
+
49
+ 1. Lê um arquivo `.zip`;
50
+ 2. Valida e sanitiza cada entrada (remove caracteres proibidos e limita o tamanho);
51
+ 3. Reescreve o ZIP com nomes seguros e com encoding correto (UTF-8);
52
+ 4. Extrai os arquivos de forma determinística.
53
+
54
+ ---
55
+
56
+ ## ✨ Funcionalidades
57
+
58
+ - ✅ Correção de nomes em entradas de ZIP;
59
+ - ✅ Sanitização de caracteres inválidos no Windows;
60
+ - ✅ Limitação de tamanho de nome;
61
+ - ✅ Reescrita de ZIP com encoding correto;
62
+ - ✅ Extração com nomes sanitizados;
63
+ - ✅ Relatório de entradas reescritas/extraídas.
64
+
65
+ ---
66
+
67
+ ## 🧰 Instalação
68
+
69
+ ```bash
70
+ pip install zipfix
71
+ ```
72
+
73
+ ou, via ferramenta de empacotamento:
74
+
75
+ ```bash
76
+ uv tool install zipfix
77
+ ```
78
+
79
+ ---
80
+
81
+ ## 🚀 Uso
82
+
83
+ ```bash
84
+ # Corrigir (reescrever) um ZIP
85
+ zipflix fix entrada.zip --out saida_corrigida.zip
86
+
87
+ # Extrair um ZIP com nomes sanitizados
88
+ zipflix extract entrada.zip destino/
89
+ ```
90
+
91
+ ### Exemplo
92
+
93
+ ```bash
94
+ zipflix fix ./downloads/arquivo.zip --out ./downloads/arquivo_corrigido.zip
95
+ zipflix extract ./downloads/arquivo_corrigido.zip ./extraido/
96
+ ```
97
+
98
+ ---
99
+
100
+ ## 🧠 Como funciona
101
+
102
+ - Usa o módulo nativo de ZIP (stdlib) que trata UTF-8;
103
+ - Emite o novo arquivo com o flag UTF-8 nas entradas;
104
+ - Sanitiza nomes proibidos no Windows (`|`, `<`, `>`, `:`, `"`, `?`, `*`, `\`) e caracteres de controle.
105
+
106
+ ---
107
+
108
+ ## 🗂️ Estrutura do projeto
109
+
110
+ ```text
111
+ C:/zipflix
112
+ ├── zipflix/
113
+ │ ├── __init__.py
114
+ │ ├── cli.py
115
+ │ ├── fixer.py
116
+ │ └── __main__.py
117
+ ├── tests/
118
+ │ └── test_fixer.py
119
+ ├── .github/
120
+ │ └── workflows/
121
+ │ └── ci.yml
122
+ ├── Makefile
123
+ ├── pyproject.toml
124
+ ├── README.md
125
+ └── LICENSE
126
+ ```
127
+
128
+ ---
129
+
130
+ ## 🔭 Próximos passos
131
+
132
+ - Testes com ZIPs reais, incluindo nomes com emoji;
133
+ - Suporte a criptografia (AES).
134
+
135
+ ---
136
+
137
+ ## 📄 Licença
138
+
139
+ MIT
140
+
141
+ ---
142
+
143
+ ## 📦 WinPathFix
144
+
145
+ **Um CLI para renomear arquivos soltos com caracteres invalidos no Windows.**
146
+
147
+ - Detecta e renomeia arquivos com caracteres proibidos (`|`, `<`, `>`, `:`, `"`, `?`, `*`, `\`).
148
+ - Preserva Unicode e estrutura de pastas.
149
+ - Limita tamanho de nome a 255 caracteres por segmento.
150
+
151
+ ```bash
152
+ winpathfix fix ./pastainvalida/
153
+ ```
154
+
155
+ ---
156
+
157
+ ## 📦 PyPI
158
+
159
+ ```bash
160
+ pip install zipfix
161
+ ```
162
+
163
+ ou
164
+
165
+ ```bash
166
+ pip install git+https://github.com/SouzaF19/zipfix.git
167
+ ```
168
+
169
+ ---
170
+
171
+ ## 📄 Licença
172
+
173
+ MIT — ver [LICENSE](LICENSE).
174
+
175
+ ---
176
+
177
+ ## 📬 Contato
178
+
179
+ - GitHub: [@SouzaF19](https://github.com/SouzaF19)
@@ -0,0 +1,14 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ tests/test_fixer.py
5
+ tests/test_winpathfix.py
6
+ zipfixx.egg-info/PKG-INFO
7
+ zipfixx.egg-info/SOURCES.txt
8
+ zipfixx.egg-info/dependency_links.txt
9
+ zipfixx.egg-info/entry_points.txt
10
+ zipfixx.egg-info/top_level.txt
11
+ zipflix/__init__.py
12
+ zipflix/__main__.py
13
+ zipflix/cli.py
14
+ zipflix/fixer.py
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ zipfix = zipflix.cli:main
@@ -0,0 +1 @@
1
+ zipflix
@@ -0,0 +1,3 @@
1
+ """ZipFix - CLI para correção e extração segura de arquivos ZIP com nomes Unicode."""
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1,4 @@
1
+ from zipflix.cli import main
2
+
3
+ if __name__ == "__main__":
4
+ raise SystemExit(main())
@@ -0,0 +1,59 @@
1
+ """Interface de linha de comando do ZipFix."""
2
+
3
+ import argparse
4
+ import sys
5
+ import zipfile
6
+
7
+ from zipflix import __version__
8
+ from zipflix import fixer
9
+
10
+
11
+ def _build_parser() -> argparse.ArgumentParser:
12
+ parser = argparse.ArgumentParser(
13
+ prog="zipflix",
14
+ description="Corrige e extrai arquivos ZIP com nomes Unicode no Windows.",
15
+ )
16
+ parser.add_argument("--version", action="version", version=f"zipflix {__version__}")
17
+ sub = parser.add_subparsers(dest="command", required=True)
18
+
19
+ p_fix = sub.add_parser("fix", help="Reescreve um ZIP com nomes sanitizados.")
20
+ p_fix.add_argument("input", help="Caminho do ZIP de entrada.")
21
+ p_fix.add_argument("--out", required=True, help="Caminho de saída do ZIP corrigido.")
22
+
23
+ p_extract = sub.add_parser("extract", help="Extrai um ZIP com nomes sanitizados.")
24
+ p_extract.add_argument("input", help="Caminho do ZIP de entrada.")
25
+ p_extract.add_argument("dest", help="Diretório de destino da extração.")
26
+
27
+ return parser
28
+
29
+
30
+ def main(argv=None) -> int:
31
+ for stream in (sys.stdout, sys.stderr):
32
+ try:
33
+ stream.reconfigure(encoding="utf-8")
34
+ except Exception:
35
+ pass
36
+ parser = _build_parser()
37
+ args = parser.parse_args(argv)
38
+
39
+ try:
40
+ if args.command == "fix":
41
+ results = fixer.fix_zip(args.input, args.out)
42
+ print(f"OK: {len(results)} entries reescritas -> {args.out}")
43
+ for orig, new in results:
44
+ print(f" {orig} ==> {new}")
45
+ return 0
46
+
47
+ if args.command == "extract":
48
+ count = fixer.extract_zip(args.input, args.dest)
49
+ print(f"OK: {count} entries extraidos para {args.dest}")
50
+ return 0
51
+ except (OSError, ValueError, zipfile.BadZipFile) as exc:
52
+ print(f"zipflix: erro: {exc}", file=sys.stderr)
53
+ return 1
54
+
55
+ return 2
56
+
57
+
58
+ if __name__ == "__main__":
59
+ raise SystemExit(main())
@@ -0,0 +1,84 @@
1
+ """Correção de nomes em arquivos ZIP com suporte a Unicode."""
2
+
3
+ import os
4
+ import re
5
+ import shutil
6
+ import zipfile
7
+
8
+ INVALID_CHARS = re.compile(r'[<>:\"|?*\x00-\x1f]')
9
+ MAX_NAME_LENGTH = 255
10
+
11
+
12
+ def sanitize_name(name: str) -> str:
13
+ r"""Remove caracteres proibidos no Windows, descarta segmentos perigosos e limita tamanhos.
14
+
15
+ - Normaliza `\` -> `/`.
16
+ - Remove caracteres inválidos (<>:"/\|?* e controle) e descarta segmentos vazios/`.`/`..`.
17
+ - Limita cada componente a `MAX_NAME_LENGTH`.
18
+ """
19
+ cleaned = name.replace("\\", "/")
20
+ trailing = cleaned.endswith("/")
21
+ parts = []
22
+ for part in cleaned.split("/"):
23
+ p = INVALID_CHARS.sub("", part).strip()
24
+ if p and p not in (".", ".."):
25
+ parts.append(p)
26
+ cleaned = "/".join(parts)
27
+ cleaned = cleaned.rstrip(".")
28
+ segments = cleaned.split("/") if cleaned else []
29
+ cleaned = "/".join(seg[:MAX_NAME_LENGTH].rstrip() for seg in segments)
30
+ if trailing and cleaned and not cleaned.endswith("/"):
31
+ cleaned = cleaned + "/"
32
+ return cleaned
33
+
34
+
35
+ def read_entries(path: str) -> list:
36
+ with zipfile.ZipFile(path, "r") as zf:
37
+ return zf.infolist()
38
+
39
+
40
+ def _clean_entries(path: str) -> list:
41
+ cleaned = []
42
+ seen = set()
43
+ with zipfile.ZipFile(path, "r") as zf:
44
+ for info in zf.infolist():
45
+ name = sanitize_name(info.filename)
46
+ if not name or name in seen:
47
+ continue
48
+ seen.add(name)
49
+ cleaned.append((name, info))
50
+ return cleaned
51
+
52
+
53
+ def fix_zip(src: str, dst: str) -> list:
54
+ """Reescreve o ZIP de entrada com nomes sanitizados (UTF-8) em um novo ZIP."""
55
+ if os.path.abspath(src) == os.path.abspath(dst):
56
+ raise ValueError("src and dst must differ")
57
+ results = []
58
+ with zipfile.ZipFile(src, "r") as zin, \
59
+ zipfile.ZipFile(dst, "w", zipfile.ZIP_DEFLATED) as zout:
60
+ for name, info in _clean_entries(src):
61
+ new_info = zipfile.ZipInfo(filename=name, date_time=info.date_time)
62
+ new_info.compress_type = info.compress_type
63
+ new_info.external_attr = info.external_attr
64
+ new_info.create_system = info.create_system
65
+ zout.writestr(new_info, zin.read(info))
66
+ results.append((info.filename, name))
67
+ return results
68
+
69
+
70
+ def extract_zip(src: str, dest: str) -> int:
71
+ """Extrai um ZIP para o diretório, com nomes sanitizados."""
72
+ os.makedirs(dest, exist_ok=True)
73
+ count = 0
74
+ with zipfile.ZipFile(src, "r") as zf:
75
+ for name, info in _clean_entries(src):
76
+ target = os.path.join(dest, *name.split("/"))
77
+ if info.filename.endswith("/"):
78
+ os.makedirs(target, exist_ok=True)
79
+ continue
80
+ os.makedirs(os.path.dirname(target) or dest, exist_ok=True)
81
+ with zf.open(info) as srcf, open(target, "wb") as f:
82
+ shutil.copyfileobj(srcf, f)
83
+ count += 1
84
+ return count