getpart.pm (9418B)
1 #*************************************************************************** 2 # _ _ ____ _ 3 # Project ___| | | | _ \| | 4 # / __| | | | |_) | | 5 # | (__| |_| | _ <| |___ 6 # \___|\___/|_| \_\_____| 7 # 8 # Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 9 # 10 # This software is licensed as described in the file COPYING, which 11 # you should have received as part of this distribution. The terms 12 # are also available at https://curl.se/docs/copyright.html. 13 # 14 # You may opt to use, copy, modify, merge, publish, distribute and/or sell 15 # copies of the Software, and permit persons to whom the Software is 16 # furnished to do so, under the terms of the COPYING file. 17 # 18 # This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 # KIND, either express or implied. 20 # 21 # SPDX-License-Identifier: curl 22 # 23 ########################################################################### 24 25 package getpart; 26 27 use strict; 28 use warnings; 29 30 BEGIN { 31 use base qw(Exporter); 32 33 our @EXPORT = qw( 34 compareparts 35 fulltest 36 getpart 37 getpartattr 38 loadarray 39 loadtest 40 partexists 41 striparray 42 writearray 43 ); 44 } 45 46 use Memoize; 47 48 my @xml; # test data file contents 49 my $xmlfile; # test data file name 50 51 my $warning=0; 52 my $trace=0; 53 54 # Normalize the part function arguments for proper caching. This includes the 55 # file name in the arguments since that is an implied parameter that affects the 56 # return value. Any error messages will only be displayed the first time, but 57 # those are disabled by default anyway, so should never been seen outside 58 # development. 59 sub normalize_part { 60 push @_, $xmlfile; 61 return join("\t", @_); 62 } 63 64 sub decode_hex { 65 my $s = $_; 66 # remove everything not hex 67 $s =~ s/[^A-Fa-f0-9]//g; 68 # encode everything 69 $s =~ s/([a-fA-F0-9][a-fA-F0-9])/chr(hex($1))/eg; 70 return $s; 71 } 72 73 sub testcaseattr { 74 my %hash; 75 for(@xml) { 76 if(($_ =~ /^ *\<testcase ([^>]*)/)) { 77 my $attr=$1; 78 while($attr =~ s/ *([^=]*)= *(\"([^\"]*)\"|([^\> ]*))//) { 79 my ($var, $cont)=($1, $2); 80 $cont =~ s/^\"(.*)\"$/$1/; 81 $hash{$var}=$cont; 82 } 83 } 84 } 85 return %hash; 86 } 87 88 sub getpartattr { 89 # if $part is undefined (ie only one argument) then 90 # return the attributes of the section 91 92 my ($section, $part)=@_; 93 94 my %hash; 95 my $inside=0; 96 97 # print "Section: $section, part: $part\n"; 98 99 for(@xml) { 100 # print "$inside: $_"; 101 if(!$inside && ($_ =~ /^ *\<$section/)) { 102 $inside++; 103 } 104 if((1 ==$inside) && ( ($_ =~ /^ *\<$part ([^>]*)/) || 105 !(defined($part)) ) 106 ) { 107 $inside++; 108 my $attr=$1; 109 110 while($attr =~ s/ *([^=]*)= *(\"([^\"]*)\"|([^\> ]*))//) { 111 my ($var, $cont)=($1, $2); 112 $cont =~ s/^\"(.*)\"$/$1/; 113 $hash{$var}=$cont; 114 } 115 last; 116 } 117 # detect end of section when part wasn't found 118 elsif((1 ==$inside) && ($_ =~ /^ *\<\/$section\>/)) { 119 last; 120 } 121 elsif((2 ==$inside) && ($_ =~ /^ *\<\/$part/)) { 122 $inside--; 123 } 124 } 125 return %hash; 126 } 127 memoize('getpartattr', NORMALIZER => 'normalize_part'); # cache each result 128 129 sub getpart { 130 my ($section, $part)=@_; 131 132 my @this; 133 my $inside=0; 134 my $hex=0; 135 my $line; 136 137 for(@xml) { 138 $line++; 139 if(!$inside && ($_ =~ /^ *\<$section/)) { 140 $inside++; 141 } 142 elsif(($inside >= 1) && ($_ =~ /^ *\<$part[ \>]/)) { 143 if($inside > 1) { 144 push @this, $_; 145 } 146 elsif($_ =~ /$part [^>]*hex=/) { 147 # attempt to detect a hex-encoded part 148 $hex=1; 149 } 150 $inside++; 151 } 152 elsif(($inside >= 2) && ($_ =~ /^ *\<\/$part[ \>]/)) { 153 if($inside > 2) { 154 push @this, $_; 155 } 156 $inside--; 157 } 158 elsif(($inside >= 1) && ($_ =~ /^ *\<\/$section/)) { 159 if($inside > 1) { 160 print STDERR "$xmlfile:$line:1: error: missing </$part> tag before </$section>\n"; 161 @this = ("format error in $xmlfile"); 162 } 163 if($trace && @this) { 164 print STDERR "*** getpart.pm: $section/$part returned data!\n"; 165 } 166 if($warning && !@this) { 167 print STDERR "*** getpart.pm: $section/$part returned empty!\n"; 168 } 169 if($hex) { 170 # decode the whole array before returning it! 171 for(@this) { 172 my $decoded = decode_hex($_); 173 $_ = $decoded; 174 } 175 } 176 return @this; 177 } 178 elsif($inside >= 2) { 179 push @this, $_; 180 } 181 } 182 if($trace && @this) { 183 # section/part has data but end of section not detected, 184 # end of file implies end of section. 185 print STDERR "*** getpart.pm: $section/$part returned data!\n"; 186 } 187 if($warning && !@this) { 188 # section/part does not exist or has no data without an end of 189 # section; end of file implies end of section. 190 print STDERR "*** getpart.pm: $section/$part returned empty!\n"; 191 } 192 return @this; 193 } 194 memoize('getpart', NORMALIZER => 'normalize_part'); # cache each result 195 196 sub partexists { 197 my ($section, $part)=@_; 198 199 my $inside = 0; 200 201 for(@xml) { 202 if(!$inside && ($_ =~ /^ *\<$section/)) { 203 $inside++; 204 } 205 elsif((1 == $inside) && ($_ =~ /^ *\<$part[ \>]/)) { 206 return 1; # exists 207 } 208 elsif((1 == $inside) && ($_ =~ /^ *\<\/$section/)) { 209 return 0; # does not exist 210 } 211 } 212 return 0; # does not exist 213 } 214 # The code currently never calls this more than once per part per file, so 215 # caching a result that will never be used again just slows things down. 216 # memoize('partexists', NORMALIZER => 'normalize_part'); # cache each result 217 218 sub loadtest { 219 my ($file)=@_; 220 221 if(defined $xmlfile && $file eq $xmlfile) { 222 # This test is already loaded 223 return 224 } 225 226 undef @xml; 227 $xmlfile = ""; 228 229 if(open(my $xmlh, "<", "$file")) { 230 binmode $xmlh; # for crapage systems, use binary 231 while(<$xmlh>) { 232 push @xml, $_; 233 } 234 close($xmlh); 235 } 236 else { 237 # failure 238 if($warning) { 239 print STDERR "file $file wouldn't open!\n"; 240 } 241 return 1; 242 } 243 $xmlfile = $file; 244 return 0; 245 } 246 247 248 # Return entire document as list of lines 249 sub fulltest { 250 return @xml; 251 } 252 253 # write the test to the given file 254 sub savetest { 255 my ($file)=@_; 256 257 if(open(my $xmlh, ">", "$file")) { 258 binmode $xmlh; # for crapage systems, use binary 259 for(@xml) { 260 print $xmlh $_; 261 } 262 close($xmlh); 263 } 264 else { 265 # failure 266 if($warning) { 267 print STDERR "file $file wouldn't open!\n"; 268 } 269 return 1; 270 } 271 return 0; 272 } 273 274 # 275 # Strip off all lines that match the specified pattern and return 276 # the new array. 277 # 278 279 sub striparray { 280 my ($pattern, $arrayref) = @_; 281 282 my @array; 283 284 for(@$arrayref) { 285 if($_ !~ /$pattern/) { 286 push @array, $_; 287 } 288 } 289 return @array; 290 } 291 292 # 293 # pass array *REFERENCES* ! 294 # 295 sub compareparts { 296 my ($firstref, $secondref)=@_; 297 298 # we cannot compare arrays index per index since with data chunks, 299 # they may not be "evenly" distributed 300 my $first = join("", @$firstref); 301 my $second = join("", @$secondref); 302 303 if($first =~ /%alternatives\[/) { 304 die "bad use of compareparts\n"; 305 } 306 307 if($second =~ /%alternatives\[([^,]*),([^\]]*)\]/) { 308 # there can be many %alternatives in this chunk, so we call 309 # this function recursively 310 my $alt = $second; 311 $alt =~ s/%alternatives\[([^,]*),([^\]]*)\]/$1/; 312 313 # check first alternative 314 { 315 my @f; 316 my @s; 317 push @f, $first; 318 push @s, $alt; 319 if(!compareparts(\@f, \@s)) { 320 return 0; 321 } 322 } 323 324 $alt = $second; 325 $alt =~ s/%alternatives\[([^,]*),([^\]]*)\]/$2/; 326 # check second alternative 327 { 328 my @f; 329 my @s; 330 push @f, $first; 331 push @s, $alt; 332 if(!compareparts(\@f, \@s)) { 333 return 0; 334 } 335 } 336 337 # neither matched 338 return 1; 339 } 340 341 if($first ne $second) { 342 return 1; 343 } 344 345 return 0; 346 } 347 348 # 349 # Write a given array to the specified file 350 # 351 sub writearray { 352 my ($filename, $arrayref)=@_; 353 354 open(my $temp, ">", "$filename") || die "Failure writing file"; 355 binmode($temp,":raw"); # Cygwin fix by Kevin Roth 356 for(@$arrayref) { 357 print $temp $_; 358 } 359 close($temp) || die "Failure writing file"; 360 } 361 362 # 363 # Load a specified file and return it as an array 364 # 365 sub loadarray { 366 my ($filename)=@_; 367 my @array; 368 369 if(open(my $temp, "<", "$filename")) { 370 while(<$temp>) { 371 push @array, $_; 372 } 373 close($temp); 374 } 375 return @array; 376 } 377 378 379 1;