end
class Manifest
- STRICT_STREAM_TOKEN_REGEXP = /^(\.)(\/[^\/\s]+)*$/
- STRICT_FILE_TOKEN_REGEXP = /^[[:digit:]]+:[[:digit:]]+:([^\s\/]+(\/[^\s\/]+)*)$/
+ STRICT_STREAM_TOKEN_REGEXP = /^(\.)(\/[^\/\t\v\n\r]+)*$/
+ STRICT_FILE_TOKEN_REGEXP = /^[[:digit:]]+:[[:digit:]]+:([^\t\v\n\r\/]+(\/[^\t\v\n\r\/]+)*)$/
EMPTY_DOT_FILE_TOKEN_REGEXP = /^0:0:\.$/
# Class to parse a manifest text and provide common views of that data.
end
end
- def self.unescape(s, except=[])
+ def self.unescape(s)
return nil if s.nil?
# Parse backslash escapes in a Keep manifest stream or file name.
s.gsub(/\\(\\|[0-7]{3})/) do |_|
- if $1 == '\\'
+ case $1
+ when '\\'
'\\'
- elsif except.include? $1
- $1
else
$1.to_i(8).chr
end
count = 0
word = words.shift
- unescaped_word = unescape(word, except=["040"])
+ unescaped_word = unescape(word)
count += 1 if unescaped_word =~ STRICT_STREAM_TOKEN_REGEXP and unescaped_word !~ /\/\.\.?(\/|$)/
raise ArgumentError.new "Manifest invalid for stream #{line_count}: missing or invalid stream name #{word.inspect if word}" if count != 1
count = 0
while unescape(word) =~ EMPTY_DOT_FILE_TOKEN_REGEXP or
- (unescape(word, except=["040"]) =~ STRICT_FILE_TOKEN_REGEXP and ($~[1].split('/') & ['..', '.']).empty?)
+ (unescape(word) =~ STRICT_FILE_TOKEN_REGEXP and ($~[1].split('/') & ['..', '.']).empty?)
word = words.shift
count += 1
end
[false, ". d41d8cd98f00b204e9800998ecf8427e+0 0:0:\\057foo/bar\n",
"Manifest invalid for stream 1: invalid file token \"0:0:\\\\057foo/bar\""],
[true, ". d41d8cd98f00b204e9800998ecf8427e+0 0:0:\\134057foo/bar\n"],
- [false, ". d41d8cd98f00b204e9800998ecf8427e+0 \\040:\\040:foo.txt\n"],
+ [false, ". d41d8cd98f00b204e9800998ecf8427e+0 \\040:\\040:foo.txt\n",
+ "Manifest invalid for stream 1: invalid file token \"\\\\040:\\\\040:foo.txt\""],
].each do |ok, manifest, expected_error=nil|
define_method "test_validate manifest #{manifest.inspect}" do
assert_equal ok, Keep::Manifest.valid?(manifest)