-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathunrar_wrapper.py
executable file
·213 lines (171 loc) · 6.45 KB
/
unrar_wrapper.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
#!/usr/bin/python3
"""
UnRAR wrapper is a wrapper python script that transforms the basic UnRAR
commands to unar and lsar calling in order to provide a backwards
compatibility.
It supports only a subset of the UnRAR commands and options:
Supported commands: l[t[a],b], t, v[t[a],b], x.
Supported options: -o+, -o-, -or, -p
Other: files, @listfiles and path_to_extract (only for unar)
Return codes: 0 (success), 1 (error), 2 (invalid argument)
"""
import argparse
import subprocess
import sys
def parse_args():
"""Create a parser and parse UnRAR synopsis.
The UnRAR synopsis is as follows:
unrar command [option1] [optionN] archive [files...] [@list-files...]
[path_to_extract/]
Support the following commands: l[t[a],b], t, v[t[a],b], x.
Support the following options: -o+, -o-, -or, -p
Args:
Returns:
A namespace class that holds parsed arguments
Raises:
"""
parser = argparse.ArgumentParser(description="Transforms the basic UnRAR "
"commands to unar and lsar "
"calling in order to provide "
"a backwards compatibility")
parser.add_argument('command',
choices=['l', 'lt', 'lta', 'lb', 't', 'v', 'vt',
'vta', 'vb', 'x'],
help="UnRAR command")
parser.add_argument('-o',
dest='overwrite',
choices=['+', '-', 'r'],
help="'-o+' Set the overwrite mode, '-o-' Unset the "
"overwrite mode,'-or' Rename files automatically")
parser.add_argument('-p',
dest='password',
help='-p[password] Set password.')
parser.add_argument('archive',
help='The path to the archive')
parser.add_argument('rest',
nargs='*',
help="[files...] [@list-files...] [path_to_extract/]")
args = parser.parse_args()
return args
def transform_syntax(args):
"""Transform UnRAR command and options to the Unarchiver syntax.
Args:
args: A namespace class that holds parsed UnRAR arguments
Returns:
A tuple (commands, options) with a string and a list that hold
Unarchiver command and options
Raises:
"""
opts = []
if args.command == 'x':
command = 'unar'
# '-o+' means force-overwrite
if args.overwrite == '+':
opts.append('-f')
# '-o-' means force-skip
elif args.overwrite == '-':
opts.append('-s')
# '-or' means force-rename
elif args.overwrite == 'r':
opts.append('-r')
else:
command = 'lsar'
if (args.command == 'lb' or
args.command == 'vb'):
# 'lb' and 'vb'are translated to plain 'lsar' command
pass
elif (args.command == 'l' or
args.command == 'v'):
opts.append('-l')
elif (args.command == 'lt' or
args.command == 'lta' or
args.command == 'vt' or
args.command == 'vta'):
opts.append('-L')
elif args.command == 't':
opts.append('-t')
# '-p' option makes sense for all commands
if args.password:
opts.append('-p')
opts.append(args.password)
return command, opts
def transform_list_files(list_files):
"""Return the content of the files given as a list.
If any of the files doesn't exist then the program is terminated.
Args:
list_files: a list of paths to the files
Returns:
A list of concatenated content of the files given as list_files
Raises:
"""
files = []
for f in list_files:
try:
with open(f, "r") as stream:
for line in stream:
# Remove trailing newline
files.append(line.rstrip())
except IOError:
print("Cannot open {}\nNo such file or directory".format(f),
file=sys.stderr)
sys.exit(1)
return files
def process_rest(rest):
"""Split the argument to [files], [@list-files] and [path_to_extract/].
All of these parts are optional.
Args:
rest: a list of strings with the paths to files, @list-files and
path_to_extract/
Returns:
A tuple (files, list_files, path)
- files: a list of files, that should be processed
- @list-files: a list of text files that contain a list of files that
should be processed
- path_to_extract/: a string with a directory to write the contents of
the archive to
Raises:
"""
if not rest:
return None, None, None
# UnRAR considers every item ending with '/' as a path.
# If multiple paths are present, only the last one is used.
files = []
list_files = []
path = None
for item in rest:
if item.endswith('/'):
path = item
elif item.startswith('@'):
list_files.append(item[1:])
else:
files.append(item)
return files, list_files, path
def main():
args = parse_args()
# Translate UnRAR command and options to the Unarchiver syntax
command, options = transform_syntax(args)
files = []
if args.rest and command == 'lsar':
# files, @list_files and path are not supported for lsar
print("Warning: [files...], [@list_files...] and [path_to_extract/]"
" are not supported for listing and testing. These parameters"
" are ignored.", file=sys.stderr)
if command == 'unar':
# Add '-D' option by default in order to simulate default UnRAR
# behaviour (never create a new containing directory if there is more
# than one top-level file or folder)
options.append("-D")
if args.rest:
files, list_files, path = process_rest(args.rest)
# unar can't process @list_files as is -> transform it to files
if list_files:
files = files + transform_list_files(list_files)
# Transform path to unar syntax ("-o DIRECTORY")
if path:
options.extend(["-o", path])
# Call Unarchiver
rc = subprocess.call([command] + options + [args.archive] + files)
return rc
if __name__ == "__main__":
return_code = main()
sys.exit(return_code)