Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-ports
Path: blob/main/Tools/scripts/sunshar.rb
18157 views
1
#!/usr/bin/env ruby
2
# -*- ruby -*-
3
#
4
# Copyright (c) 2001-2004 Akinori MUSHA
5
#
6
# All rights reserved.
7
#
8
# Redistribution and use in source and binary forms, with or without
9
# modification, are permitted provided that the following conditions
10
# are met:
11
# 1. Redistributions of source code must retain the above copyright
12
# notice, this list of conditions and the following disclaimer.
13
# 2. Redistributions in binary form must reproduce the above copyright
14
# notice, this list of conditions and the following disclaimer in the
15
# documentation and/or other materials provided with the distribution.
16
#
17
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27
# SUCH DAMAGE.
28
29
RCS_ID = %q$Idaemons: /home/cvs/sunshar/sunshar.rb,v 1.13 2004/02/28 14:15:47 knu Exp $
30
RCS_REVISION = RCS_ID.split[2]
31
MYNAME = File.basename($0)
32
33
require 'optparse'
34
require 'fileutils'
35
require 'shellwords'
36
require 'stringio'
37
38
$USAGE = 'usage'
39
40
$strip_level = 0
41
$force = false
42
$dryrun = false
43
$quiet = false
44
$dir = nil
45
46
def info(*s)
47
puts(*s) unless $quiet
48
end
49
50
def usage
51
print <<-EOF
52
#{MYNAME} rev.#{RCS_REVISION}
53
54
usage: #{MYNAME} [-fnq] [-p level] [file]
55
#{MYNAME} -h
56
-d dir chdir -- chdir to dir before extracting files
57
-f force -- allow overwriting, ignore errors
58
-h help -- show this help
59
-n dry run -- show what would have been extracted
60
-p N strip -- strip N levels from pathnames (cf. patch(1)\'s -p)
61
-q quiet -- be quiet
62
EOF
63
end
64
65
def main
66
params = ARGV.getopts("fhnq", "d:", "p:")
67
68
if params['h']
69
usage
70
exit 0
71
end
72
73
if params['f']
74
$force = true
75
end
76
77
if params['n']
78
$dryrun = true
79
end
80
81
if params['q']
82
$quiet = true
83
end
84
85
$dir = params['d'] || '.'
86
87
if not params['p'].nil?
88
$strip_level = params['p'].to_i rescue -1
89
90
if $strip_level < 0
91
STDERR.puts "negative value ignored: #{params['p']}"
92
end
93
end
94
95
nerrors = 0
96
97
if ARGV.empty?
98
info "extracting files from stdin into #{$dir}"
99
100
begin
101
Dir.chdir($dir) {
102
unshar_stream(STDIN)
103
}
104
105
info "done."
106
rescue => e
107
STDERR.puts "error in extracting stdin: #{e.message}"
108
nerrors += 1
109
end
110
else
111
for file in ARGV
112
info "extracting files from #{file} into #{$dir}"
113
114
begin
115
File.open(file) do |f|
116
Dir.chdir($dir) {
117
unshar_stream(f)
118
}
119
end
120
121
info "done."
122
rescue => e
123
STDERR.puts "error in extracting #{file}: #{e.message}"
124
nerrors += 1
125
end
126
end
127
end
128
129
exit nerrors
130
end
131
132
def unshar_stream(io)
133
e = nil
134
135
while line = io.gets
136
if /^(\s*)\# This is a shell archive/ =~ line
137
indent = $1.length
138
break
139
end
140
end
141
142
if io.eof?
143
raise "not a shell archive."
144
end
145
146
f = nil
147
prefix = nil
148
file = nil
149
boundary = nil
150
151
while line = io.gets
152
line.slice!(0, indent)
153
154
if f
155
if line.strip == boundary
156
f.close
157
f = nil
158
next
159
end
160
161
if line.sub!(/^#{Regexp.quote(prefix)}/, '')
162
f.print line
163
else
164
raise "line #{io.lineno}: broken archive: #{line}"
165
end
166
167
next
168
end
169
170
case line
171
when /^exit\s*$/
172
break
173
when /^echo\s+(.+)$/
174
# info $1
175
when /^mkdir\s+(?:-p\s+)?(.+)$/
176
dir = nil
177
178
Shellwords.shellwords($1).each do |word|
179
if /^[^\-]/ =~ word
180
dir = word
181
break
182
end
183
end
184
185
next if dir.nil?
186
187
dir = strip_filename(dir.strip + '/')
188
if dir.chomp('/').empty?
189
next
190
end
191
192
begin
193
FileUtils.mkdir_p(dir) unless $dryrun
194
info "c - #{dir}"
195
rescue => e
196
info "c - #{dir} ... failed: #{e.message}"
197
raise e
198
end
199
when /sed\s+(.+)>(.+)<<(.+)/
200
prefix = Shellwords.shellwords($1).first
201
file = Shellwords.shellwords($2).first
202
boundary = Shellwords.shellwords($3).first
203
204
next unless prefix && file && boundary
205
206
if /s(.)\^(.*)\1\1/ =~ prefix
207
prefix = $2
208
else
209
next
210
end
211
212
file = strip_filename(file)
213
214
next if file.empty? || boundary.empty?
215
216
overwrite = false
217
218
if File.exist?(file)
219
if $force
220
overwrite = true
221
else
222
info "x - #{file} ... skipped"
223
next
224
end
225
end
226
227
dir = File.dirname(file)
228
229
if !File.directory?(dir + "/.")
230
begin
231
FileUtils.mkdir_p(dir) unless $dryrun
232
info "d - #{dir}"
233
rescue => e
234
info "d - #{dir} ... failed: #{e.message}"
235
raise e
236
end
237
end
238
239
begin
240
f = $dryrun ? StringIO.new : File.open(file, 'w')
241
if overwrite
242
info "x - #{file} ... overwritten"
243
else
244
info "x - #{file}"
245
end
246
rescue => e
247
info "x - #{file} ... failed! (#{e.message})"
248
249
if $force
250
f = nil
251
next
252
else
253
raise e
254
end
255
end
256
end
257
end
258
259
raise e if e
260
end
261
262
def strip_filename(file)
263
sfile = file.gsub(%r"/{2,}", "/")
264
265
if 0 < $strip_level
266
sfile.sub!(%r"^([^/]*/){1,#{$strip_level}}", '')
267
end
268
269
case sfile
270
when %r"^[~/]"
271
raise "reference to absolute directory: #{file} (use -p N)"
272
when %r"(^|/)\.\.(?:/|$)"
273
raise "reference to parent directory: #{file} (use -p N)"
274
end
275
276
sfile
277
end
278
279
def signal_handler(sig)
280
info "\nInterrupted."
281
282
exit 255
283
end
284
285
if $0 == __FILE__
286
for sig in [2, 3, 15]
287
trap(sig) do
288
signal_handler(sig)
289
end
290
end
291
292
main
293
end
294
295