jacktang-hacker-slides 1.0.1
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.
- data/LICENSE +21 -0
- data/README.textile +58 -0
- data/bin/hacker-slides +137 -0
- data/bundles/s5-simple.bundle +0 -0
- data/lib/hacker_slides.rb +2 -0
- data/lib/hacker_slides/bundle.rb +114 -0
- data/lib/hacker_slides/engine.rb +5 -0
- data/lib/hacker_slides/engine/base.rb +6 -0
- data/lib/hacker_slides/engine/markup_engine.rb +36 -0
- data/lib/hacker_slides/engine/render_engine.rb +12 -0
- data/lib/hacker_slides/engine/s5_slides_generator.rb +33 -0
- data/lib/hacker_slides/engine/slides_engine.rb +157 -0
- data/lib/hacker_slides/helper.rb +1 -0
- data/lib/hacker_slides/helper/content_helper.rb +46 -0
- data/lib/hacker_slides/presentation.rb +35 -0
- data/lib/hacker_slides/version.rb +13 -0
- data/lib/minitar.rb +124 -0
- data/lib/minitar/input.rb +149 -0
- data/lib/minitar/output.rb +206 -0
- data/lib/minitar/posix_header.rb +136 -0
- data/lib/minitar/reader.rb +199 -0
- data/lib/minitar/writer.rb +206 -0
- data/samples/sample.markdown +33 -0
- data/samples/sample.textile +29 -0
- metadata +104 -0
@@ -0,0 +1,136 @@
|
|
1
|
+
module Archive
|
2
|
+
module Tar
|
3
|
+
# = Archive::Tar::PosixHeader
|
4
|
+
# Implements the POSIX tar header as a Ruby class. The structure of
|
5
|
+
# the POSIX tar header is:
|
6
|
+
#
|
7
|
+
# struct tarfile_entry_posix
|
8
|
+
# { // pack/unpack
|
9
|
+
# char name[100]; // ASCII (+ Z unless filled) a100/Z100
|
10
|
+
# char mode[8]; // 0 padded, octal, null a8 /A8
|
11
|
+
# char uid[8]; // ditto a8 /A8
|
12
|
+
# char gid[8]; // ditto a8 /A8
|
13
|
+
# char size[12]; // 0 padded, octal, null a12 /A12
|
14
|
+
# char mtime[12]; // 0 padded, octal, null a12 /A12
|
15
|
+
# char checksum[8]; // 0 padded, octal, null, space a8 /A8
|
16
|
+
# char typeflag[1]; // see below a /a
|
17
|
+
# char linkname[100]; // ASCII + (Z unless filled) a100/Z100
|
18
|
+
# char magic[6]; // "ustar\0" a6 /A6
|
19
|
+
# char version[2]; // "00" a2 /A2
|
20
|
+
# char uname[32]; // ASCIIZ a32 /Z32
|
21
|
+
# char gname[32]; // ASCIIZ a32 /Z32
|
22
|
+
# char devmajor[8]; // 0 padded, octal, null a8 /A8
|
23
|
+
# char devminor[8]; // 0 padded, octal, null a8 /A8
|
24
|
+
# char prefix[155]; // ASCII (+ Z unless filled) a155/Z155
|
25
|
+
# };
|
26
|
+
#
|
27
|
+
# The +typeflag+ may be one of the following known values:
|
28
|
+
#
|
29
|
+
# <tt>"0"</tt>:: Regular file. NULL should be treated as a synonym, for
|
30
|
+
# compatibility purposes.
|
31
|
+
# <tt>"1"</tt>:: Hard link.
|
32
|
+
# <tt>"2"</tt>:: Symbolic link.
|
33
|
+
# <tt>"3"</tt>:: Character device node.
|
34
|
+
# <tt>"4"</tt>:: Block device node.
|
35
|
+
# <tt>"5"</tt>:: Directory.
|
36
|
+
# <tt>"6"</tt>:: FIFO node.
|
37
|
+
# <tt>"7"</tt>:: Reserved.
|
38
|
+
#
|
39
|
+
# POSIX indicates that "A POSIX-compliant implementation must treat any
|
40
|
+
# unrecognized typeflag value as a regular file."
|
41
|
+
class PosixHeader
|
42
|
+
FIELDS = %w(name mode uid gid size mtime checksum typeflag linkname) +
|
43
|
+
%w(magic version uname gname devmajor devminor prefix)
|
44
|
+
|
45
|
+
FIELDS.each { |field| attr_reader field.intern }
|
46
|
+
|
47
|
+
HEADER_PACK_FORMAT = "a100a8a8a8a12a12a7aaa100a6a2a32a32a8a8a155"
|
48
|
+
HEADER_UNPACK_FORMAT = "Z100A8A8A8A12A12A8aZ100A6A2Z32Z32A8A8Z155"
|
49
|
+
|
50
|
+
# Creates a new PosixHeader from a data stream.
|
51
|
+
def self.new_from_stream(stream)
|
52
|
+
data = stream.read(512)
|
53
|
+
fields = data.unpack(HEADER_UNPACK_FORMAT)
|
54
|
+
name = fields.shift
|
55
|
+
mode = fields.shift.oct
|
56
|
+
uid = fields.shift.oct
|
57
|
+
gid = fields.shift.oct
|
58
|
+
size = fields.shift.oct
|
59
|
+
mtime = fields.shift.oct
|
60
|
+
checksum = fields.shift.oct
|
61
|
+
typeflag = fields.shift
|
62
|
+
linkname = fields.shift
|
63
|
+
magic = fields.shift
|
64
|
+
version = fields.shift.oct
|
65
|
+
uname = fields.shift
|
66
|
+
gname = fields.shift
|
67
|
+
devmajor = fields.shift.oct
|
68
|
+
devminor = fields.shift.oct
|
69
|
+
prefix = fields.shift
|
70
|
+
|
71
|
+
empty = (data == "\0" * 512)
|
72
|
+
|
73
|
+
new(:name => name, :mode => mode, :uid => uid, :gid => gid,
|
74
|
+
:size => size, :mtime => mtime, :checksum => checksum,
|
75
|
+
:typeflag => typeflag, :magic => magic, :version => version,
|
76
|
+
:uname => uname, :gname => gname, :devmajor => devmajor,
|
77
|
+
:devminor => devminor, :prefix => prefix, :empty => empty)
|
78
|
+
end
|
79
|
+
|
80
|
+
# Creates a new PosixHeader. A PosixHeader cannot be created unless the
|
81
|
+
# #name, #size, #prefix, and #mode are provided.
|
82
|
+
def initialize(vals)
|
83
|
+
unless vals[:name] && vals[:size] && vals[:prefix] && vals[:mode]
|
84
|
+
raise ArgumentError
|
85
|
+
end
|
86
|
+
|
87
|
+
vals[:mtime] ||= 0
|
88
|
+
vals[:checksum] ||= ""
|
89
|
+
vals[:typeflag] ||= "0"
|
90
|
+
vals[:magic] ||= "ustar"
|
91
|
+
vals[:version] ||= "00"
|
92
|
+
|
93
|
+
FIELDS.each do |field|
|
94
|
+
instance_variable_set("@#{field}", vals[field.intern])
|
95
|
+
end
|
96
|
+
@empty = vals[:empty]
|
97
|
+
end
|
98
|
+
|
99
|
+
def empty?
|
100
|
+
@empty
|
101
|
+
end
|
102
|
+
|
103
|
+
def to_s
|
104
|
+
update_checksum
|
105
|
+
header(@checksum)
|
106
|
+
end
|
107
|
+
|
108
|
+
# Update the checksum field.
|
109
|
+
def update_checksum
|
110
|
+
hh = header(" " * 8)
|
111
|
+
@checksum = oct(calculate_checksum(hh), 6)
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
def oct(num, len)
|
116
|
+
if num.nil?
|
117
|
+
"\0" * (len + 1)
|
118
|
+
else
|
119
|
+
"%0#{len}o" % num
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def calculate_checksum(hdr)
|
124
|
+
hdr.unpack("C*").inject { |aa, bb| aa + bb }
|
125
|
+
end
|
126
|
+
|
127
|
+
def header(chksum)
|
128
|
+
arr = [name, oct(mode, 7), oct(uid, 7), oct(gid, 7), oct(size, 11),
|
129
|
+
oct(mtime, 11), chksum, " ", typeflag, linkname, magic, version,
|
130
|
+
uname, gname, oct(devmajor, 7), oct(devminor, 7), prefix]
|
131
|
+
str = arr.pack(HEADER_PACK_FORMAT)
|
132
|
+
str + "\0" * ((512 - str.size) % 512)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,199 @@
|
|
1
|
+
module Archive
|
2
|
+
module Tar
|
3
|
+
module Minitar
|
4
|
+
# The class that reads a tar format archive from a data stream. The data
|
5
|
+
# stream may be sequential or random access, but certain features only work
|
6
|
+
# with random access data streams.
|
7
|
+
class Reader
|
8
|
+
# This marks the EntryStream closed for reading without closing the
|
9
|
+
# actual data stream.
|
10
|
+
module InvalidEntryStream
|
11
|
+
def read(len = nil); raise ClosedStream; end
|
12
|
+
def getc; raise ClosedStream; end
|
13
|
+
def rewind; raise ClosedStream; end
|
14
|
+
end
|
15
|
+
|
16
|
+
# EntryStreams are pseudo-streams on top of the main data stream.
|
17
|
+
class EntryStream
|
18
|
+
Archive::Tar::PosixHeader::FIELDS.each do |field|
|
19
|
+
attr_reader field.intern
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize(header, anIO)
|
23
|
+
@io = anIO
|
24
|
+
@name = header.name
|
25
|
+
@mode = header.mode
|
26
|
+
@uid = header.uid
|
27
|
+
@gid = header.gid
|
28
|
+
@size = header.size
|
29
|
+
@mtime = header.mtime
|
30
|
+
@checksum = header.checksum
|
31
|
+
@typeflag = header.typeflag
|
32
|
+
@linkname = header.linkname
|
33
|
+
@magic = header.magic
|
34
|
+
@version = header.version
|
35
|
+
@uname = header.uname
|
36
|
+
@gname = header.gname
|
37
|
+
@devmajor = header.devmajor
|
38
|
+
@devminor = header.devminor
|
39
|
+
@prefix = header.prefix
|
40
|
+
@read = 0
|
41
|
+
@orig_pos = @io.pos
|
42
|
+
end
|
43
|
+
|
44
|
+
# Reads +len+ bytes (or all remaining data) from the entry. Returns
|
45
|
+
# +nil+ if there is no more data to read.
|
46
|
+
def read(len = nil)
|
47
|
+
return nil if @read >= @size
|
48
|
+
len ||= @size - @read
|
49
|
+
max_read = [len, @size - @read].min
|
50
|
+
ret = @io.read(max_read)
|
51
|
+
@read += ret.size
|
52
|
+
ret
|
53
|
+
end
|
54
|
+
|
55
|
+
# Reads one byte from the entry. Returns +nil+ if there is no more data
|
56
|
+
# to read.
|
57
|
+
def getc
|
58
|
+
return nil if @read >= @size
|
59
|
+
ret = @io.getc
|
60
|
+
@read += 1 if ret
|
61
|
+
ret
|
62
|
+
end
|
63
|
+
|
64
|
+
# Returns +true+ if the entry represents a directory.
|
65
|
+
def directory?
|
66
|
+
@typeflag == "5"
|
67
|
+
end
|
68
|
+
alias_method :directory, :directory?
|
69
|
+
|
70
|
+
# Returns +true+ if the entry represents a plain file.
|
71
|
+
def file?
|
72
|
+
@typeflag == "0"
|
73
|
+
end
|
74
|
+
alias_method :file, :file?
|
75
|
+
|
76
|
+
# Returns +true+ if the current read pointer is at the end of the
|
77
|
+
# EntryStream data.
|
78
|
+
def eof?
|
79
|
+
@read >= @size
|
80
|
+
end
|
81
|
+
|
82
|
+
# Returns the current read pointer in the EntryStream.
|
83
|
+
def pos
|
84
|
+
@read
|
85
|
+
end
|
86
|
+
|
87
|
+
# Sets the current read pointer to the beginning of the EntryStream.
|
88
|
+
def rewind
|
89
|
+
raise NonSeekableStream unless @io.respond_to?(:pos=)
|
90
|
+
@io.pos = @orig_pos
|
91
|
+
@read = 0
|
92
|
+
end
|
93
|
+
|
94
|
+
def bytes_read
|
95
|
+
@read
|
96
|
+
end
|
97
|
+
|
98
|
+
# Returns the full and proper name of the entry.
|
99
|
+
def full_name
|
100
|
+
if @prefix != ""
|
101
|
+
File.join(@prefix, @name)
|
102
|
+
else
|
103
|
+
@name
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# Closes the entry.
|
108
|
+
def close
|
109
|
+
invalidate
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
def invalidate
|
114
|
+
extend InvalidEntryStream
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# With no associated block, +Reader::open+ is a synonym for
|
119
|
+
# +Reader::new+. If the optional code block is given, it will be passed
|
120
|
+
# the new _writer_ as an argument and the Reader object will
|
121
|
+
# automatically be closed when the block terminates. In this instance,
|
122
|
+
# +Reader::open+ returns the value of the block.
|
123
|
+
def self.open(anIO)
|
124
|
+
reader = Reader.new(anIO)
|
125
|
+
|
126
|
+
return reader unless block_given?
|
127
|
+
|
128
|
+
begin
|
129
|
+
res = yield reader
|
130
|
+
ensure
|
131
|
+
reader.close
|
132
|
+
end
|
133
|
+
|
134
|
+
res
|
135
|
+
end
|
136
|
+
|
137
|
+
# Creates and returns a new Reader object.
|
138
|
+
def initialize(anIO)
|
139
|
+
@io = anIO
|
140
|
+
@init_pos = anIO.pos
|
141
|
+
end
|
142
|
+
|
143
|
+
# Iterates through each entry in the data stream.
|
144
|
+
def each(&block)
|
145
|
+
each_entry(&block)
|
146
|
+
end
|
147
|
+
|
148
|
+
# Resets the read pointer to the beginning of data stream. Do not call
|
149
|
+
# this during a #each or #each_entry iteration. This only works with
|
150
|
+
# random access data streams that respond to #rewind and #pos.
|
151
|
+
def rewind
|
152
|
+
if @init_pos == 0
|
153
|
+
raise NonSeekableStream unless @io.respond_to?(:rewind)
|
154
|
+
@io.rewind
|
155
|
+
else
|
156
|
+
raise NonSeekableStream unless @io.respond_to?(:pos=)
|
157
|
+
@io.pos = @init_pos
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
# Iterates through each entry in the data stream.
|
162
|
+
def each_entry
|
163
|
+
loop do
|
164
|
+
return if @io.eof?
|
165
|
+
|
166
|
+
header = Archive::Tar::PosixHeader.new_from_stream(@io)
|
167
|
+
return if header.empty?
|
168
|
+
|
169
|
+
entry = EntryStream.new(header, @io)
|
170
|
+
size = entry.size
|
171
|
+
|
172
|
+
yield entry
|
173
|
+
|
174
|
+
skip = (512 - (size % 512)) % 512
|
175
|
+
|
176
|
+
if @io.respond_to?(:seek)
|
177
|
+
# avoid reading...
|
178
|
+
@io.seek(size - entry.bytes_read, IO::SEEK_CUR)
|
179
|
+
else
|
180
|
+
pending = size - entry.bytes_read
|
181
|
+
while pending > 0
|
182
|
+
bread = @io.read([pending, 4096].min).size
|
183
|
+
raise UnexpectedEOF if @io.eof?
|
184
|
+
pending -= bread
|
185
|
+
end
|
186
|
+
end
|
187
|
+
@io.read(skip) # discard trailing zeros
|
188
|
+
# make sure nobody can use #read, #getc or #rewind anymore
|
189
|
+
entry.close
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def close
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
@@ -0,0 +1,206 @@
|
|
1
|
+
module Archive
|
2
|
+
module Tar
|
3
|
+
module Minitar
|
4
|
+
|
5
|
+
# The class that writes a tar format archive to a data stream.
|
6
|
+
class Writer
|
7
|
+
# A stream wrapper that can only be written to. Any attempt to read
|
8
|
+
# from this restricted stream will result in a NameError being thrown.
|
9
|
+
class RestrictedStream
|
10
|
+
def initialize(anIO)
|
11
|
+
@io = anIO
|
12
|
+
end
|
13
|
+
|
14
|
+
def write(data)
|
15
|
+
@io.write(data)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# A RestrictedStream that also has a size limit.
|
20
|
+
class BoundedStream < Archive::Tar::Minitar::Writer::RestrictedStream
|
21
|
+
# The exception raised when the user attempts to write more data to
|
22
|
+
# a BoundedStream than has been allocated.
|
23
|
+
class FileOverflow < RuntimeError; end
|
24
|
+
|
25
|
+
# The maximum number of bytes that may be written to this data
|
26
|
+
# stream.
|
27
|
+
attr_reader :limit
|
28
|
+
# The current total number of bytes written to this data stream.
|
29
|
+
attr_reader :written
|
30
|
+
|
31
|
+
def initialize(io, limit)
|
32
|
+
@io = io
|
33
|
+
@limit = limit
|
34
|
+
@written = 0
|
35
|
+
end
|
36
|
+
|
37
|
+
def write(data)
|
38
|
+
raise FileOverflow if (data.size + @written) > @limit
|
39
|
+
@io.write(data)
|
40
|
+
@written += data.size
|
41
|
+
data.size
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# With no associated block, +Writer::open+ is a synonym for
|
46
|
+
# +Writer::new+. If the optional code block is given, it will be
|
47
|
+
# passed the new _writer_ as an argument and the Writer object will
|
48
|
+
# automatically be closed when the block terminates. In this instance,
|
49
|
+
# +Writer::open+ returns the value of the block.
|
50
|
+
def self.open(anIO)
|
51
|
+
writer = Writer.new(anIO)
|
52
|
+
|
53
|
+
return writer unless block_given?
|
54
|
+
|
55
|
+
begin
|
56
|
+
res = yield writer
|
57
|
+
ensure
|
58
|
+
writer.close
|
59
|
+
end
|
60
|
+
|
61
|
+
res
|
62
|
+
end
|
63
|
+
|
64
|
+
# Creates and returns a new Writer object.
|
65
|
+
def initialize(anIO)
|
66
|
+
@io = anIO
|
67
|
+
@closed = false
|
68
|
+
end
|
69
|
+
|
70
|
+
# Adds a file to the archive as +name+. +opts+ must contain the
|
71
|
+
# following values:
|
72
|
+
#
|
73
|
+
# <tt>:mode</tt>:: The Unix file permissions mode value.
|
74
|
+
# <tt>:size</tt>:: The size, in bytes.
|
75
|
+
#
|
76
|
+
# +opts+ may contain the following values:
|
77
|
+
#
|
78
|
+
# <tt>:uid</tt>: The Unix file owner user ID number.
|
79
|
+
# <tt>:gid</tt>: The Unix file owner group ID number.
|
80
|
+
# <tt>:mtime</tt>:: The *integer* modification time value.
|
81
|
+
#
|
82
|
+
# It will not be possible to add more than <tt>opts[:size]</tt> bytes
|
83
|
+
# to the file.
|
84
|
+
def add_file_simple(name, opts = {}) # :yields BoundedStream:
|
85
|
+
raise Archive::Tar::Minitar::BlockRequired unless block_given?
|
86
|
+
raise Archive::Tar::ClosedStream if @closed
|
87
|
+
|
88
|
+
name, prefix = split_name(name)
|
89
|
+
|
90
|
+
header = { :name => name, :mode => opts[:mode], :mtime => opts[:mtime],
|
91
|
+
:size => opts[:size], :gid => opts[:gid], :uid => opts[:uid],
|
92
|
+
:prefix => prefix }
|
93
|
+
header = Archive::Tar::PosixHeader.new(header).to_s
|
94
|
+
@io.write(header)
|
95
|
+
|
96
|
+
os = BoundedStream.new(@io, opts[:size])
|
97
|
+
yield os
|
98
|
+
# FIXME: what if an exception is raised in the block?
|
99
|
+
|
100
|
+
min_padding = opts[:size] - os.written
|
101
|
+
@io.write("\0" * min_padding)
|
102
|
+
remainder = (512 - (opts[:size] % 512)) % 512
|
103
|
+
@io.write("\0" * remainder)
|
104
|
+
end
|
105
|
+
|
106
|
+
# Adds a file to the archive as +name+. +opts+ must contain the
|
107
|
+
# following value:
|
108
|
+
#
|
109
|
+
# <tt>:mode</tt>:: The Unix file permissions mode value.
|
110
|
+
#
|
111
|
+
# +opts+ may contain the following values:
|
112
|
+
#
|
113
|
+
# <tt>:uid</tt>: The Unix file owner user ID number.
|
114
|
+
# <tt>:gid</tt>: The Unix file owner group ID number.
|
115
|
+
# <tt>:mtime</tt>:: The *integer* modification time value.
|
116
|
+
#
|
117
|
+
# The file's size will be determined from the amount of data written
|
118
|
+
# to the stream.
|
119
|
+
#
|
120
|
+
# For #add_file to be used, the Archive::Tar::Minitar::Writer must be
|
121
|
+
# wrapping a stream object that is seekable (e.g., it responds to
|
122
|
+
# #pos=). Otherwise, #add_file_simple must be used.
|
123
|
+
#
|
124
|
+
# +opts+ may be modified during the writing to the stream.
|
125
|
+
def add_file(name, opts = {}) # :yields RestrictedStream, +opts+:
|
126
|
+
raise Archive::Tar::Minitar::BlockRequired unless block_given?
|
127
|
+
raise Archive::Tar::Minitar::ClosedStream if @closed
|
128
|
+
raise Archive::Tar::Minitar::NonSeekableStream unless @io.respond_to?(:pos=)
|
129
|
+
|
130
|
+
name, prefix = split_name(name)
|
131
|
+
init_pos = @io.pos
|
132
|
+
@io.write("\0" * 512) # placeholder for the header
|
133
|
+
|
134
|
+
yield RestrictedStream.new(@io), opts
|
135
|
+
# FIXME: what if an exception is raised in the block?
|
136
|
+
|
137
|
+
size = @io.pos - (init_pos + 512)
|
138
|
+
remainder = (512 - (size % 512)) % 512
|
139
|
+
@io.write("\0" * remainder)
|
140
|
+
|
141
|
+
final_pos = @io.pos
|
142
|
+
@io.pos = init_pos
|
143
|
+
|
144
|
+
header = { :name => name, :mode => opts[:mode], :mtime => opts[:mtime],
|
145
|
+
:size => size, :gid => opts[:gid], :uid => opts[:uid],
|
146
|
+
:prefix => prefix }
|
147
|
+
header = Archive::Tar::PosixHeader.new(header).to_s
|
148
|
+
@io.write(header)
|
149
|
+
@io.pos = final_pos
|
150
|
+
end
|
151
|
+
|
152
|
+
# Creates a directory in the tar.
|
153
|
+
def mkdir(name, opts = {})
|
154
|
+
raise ClosedStream if @closed
|
155
|
+
name, prefix = split_name(name)
|
156
|
+
header = { :name => name, :mode => opts[:mode], :typeflag => "5",
|
157
|
+
:size => 0, :gid => opts[:gid], :uid => opts[:uid],
|
158
|
+
:mtime => opts[:mtime], :prefix => prefix }
|
159
|
+
header = Archive::Tar::PosixHeader.new(header).to_s
|
160
|
+
@io.write(header)
|
161
|
+
nil
|
162
|
+
end
|
163
|
+
|
164
|
+
# Passes the #flush method to the wrapped stream, used for buffered
|
165
|
+
# streams.
|
166
|
+
def flush
|
167
|
+
raise ClosedStream if @closed
|
168
|
+
@io.flush if @io.respond_to?(:flush)
|
169
|
+
end
|
170
|
+
|
171
|
+
# Closes the Writer.
|
172
|
+
def close
|
173
|
+
return if @closed
|
174
|
+
@io.write("\0" * 1024)
|
175
|
+
@closed = true
|
176
|
+
end
|
177
|
+
|
178
|
+
private
|
179
|
+
def split_name(name)
|
180
|
+
raise FileNameTooLong if name.size > 256
|
181
|
+
if name.size <= 100
|
182
|
+
prefix = ""
|
183
|
+
else
|
184
|
+
parts = name.split(/\//)
|
185
|
+
newname = parts.pop
|
186
|
+
|
187
|
+
nxt = ""
|
188
|
+
|
189
|
+
loop do
|
190
|
+
nxt = parts.pop
|
191
|
+
break if newname.size + 1 + nxt.size > 100
|
192
|
+
newname = "#{nxt}/#{newname}"
|
193
|
+
end
|
194
|
+
|
195
|
+
prefix = (parts + [nxt]).join("/")
|
196
|
+
|
197
|
+
name = newname
|
198
|
+
|
199
|
+
raise FileNameTooLong if name.size > 100 || prefix.size > 155
|
200
|
+
end
|
201
|
+
return name, prefix
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|