Jim Cownie | 5e8470a | 2013-09-27 10:38:44 +0000 | [diff] [blame] | 1 | # |
| 2 | # This is not a runnable script, it is a Perl module, a collection of variables, subroutines, etc. |
| 3 | # to be used in other scripts. |
| 4 | # |
| 5 | # To get help about exported variables and subroutines, please execute the following command: |
| 6 | # |
| 7 | # perldoc tools.pm |
| 8 | # |
| 9 | # or see POD (Plain Old Documentation) imbedded to the source... |
| 10 | # |
| 11 | # |
| 12 | #//===----------------------------------------------------------------------===// |
| 13 | #// |
| 14 | #// The LLVM Compiler Infrastructure |
| 15 | #// |
| 16 | #// This file is dual licensed under the MIT and the University of Illinois Open |
| 17 | #// Source Licenses. See LICENSE.txt for details. |
| 18 | #// |
| 19 | #//===----------------------------------------------------------------------===// |
| 20 | # |
| 21 | |
| 22 | =head1 NAME |
| 23 | |
| 24 | B<tools.pm> -- A collection of subroutines which are widely used in Perl scripts. |
| 25 | |
| 26 | =head1 SYNOPSIS |
| 27 | |
| 28 | use FindBin; |
| 29 | use lib "$FindBin::Bin/lib"; |
| 30 | use tools; |
| 31 | |
| 32 | =head1 DESCRIPTION |
| 33 | |
| 34 | B<Note:> Because this collection is small and intended for widely using in particular project, |
| 35 | all variables and functions are exported by default. |
| 36 | |
| 37 | B<Note:> I have some ideas how to improve this collection, but it is in my long-term plans. |
| 38 | Current shape is not ideal, but good enough to use. |
| 39 | |
| 40 | =cut |
| 41 | |
| 42 | package tools; |
| 43 | |
| 44 | use strict; |
| 45 | use warnings; |
| 46 | |
| 47 | use vars qw( @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS ); |
| 48 | require Exporter; |
| 49 | @ISA = qw( Exporter ); |
| 50 | |
| 51 | my @vars = qw( $tool ); |
| 52 | my @utils = qw( check_opts validate ); |
| 53 | my @opts = qw( get_options ); |
| 54 | my @print = qw( debug info warning cmdline_error runtime_error question ); |
| 55 | my @name = qw( get_vol get_dir get_file get_name get_ext cat_file cat_dir ); |
| 56 | my @file = qw( which abs_path rel_path real_path make_dir clean_dir copy_dir move_dir del_dir change_dir copy_file move_file del_file ); |
| 57 | my @io = qw( read_file write_file ); |
| 58 | my @exec = qw( execute backticks ); |
| 59 | my @string = qw{ pad }; |
| 60 | @EXPORT = ( @utils, @opts, @vars, @print, @name, @file, @io, @exec, @string ); |
| 61 | |
| 62 | use UNIVERSAL (); |
| 63 | |
| 64 | use FindBin; |
| 65 | use IO::Handle; |
| 66 | use IO::File; |
| 67 | use IO::Dir; |
| 68 | # Not available on some machines: use IO::Zlib; |
| 69 | |
| 70 | use Getopt::Long (); |
| 71 | use Pod::Usage (); |
| 72 | use Carp (); |
| 73 | use File::Copy (); |
| 74 | use File::Path (); |
| 75 | use File::Temp (); |
| 76 | use File::Spec (); |
| 77 | use POSIX qw{ :fcntl_h :errno_h }; |
| 78 | use Cwd (); |
| 79 | use Symbol (); |
| 80 | |
| 81 | use Data::Dumper; |
| 82 | |
| 83 | use vars qw( $tool $verbose $timestamps ); |
| 84 | $tool = $FindBin::Script; |
| 85 | |
| 86 | my @warning = ( sub {}, \&warning, \&runtime_error ); |
| 87 | |
| 88 | |
| 89 | sub check_opts(\%$;$) { |
| 90 | |
| 91 | my $opts = shift( @_ ); # Referense to hash containing real options and their values. |
| 92 | my $good = shift( @_ ); # Reference to an array containing all known option names. |
| 93 | my $msg = shift( @_ ); # Optional (non-mandatory) message. |
| 94 | |
| 95 | if ( not defined( $msg ) ) { |
| 96 | $msg = "unknown option(s) passed"; # Default value for $msg. |
| 97 | }; # if |
| 98 | |
| 99 | # I'll use these hashes as sets of options. |
| 100 | my %good = map( ( $_ => 1 ), @$good ); # %good now is filled with all known options. |
| 101 | my %bad; # %bad is empty. |
| 102 | |
| 103 | foreach my $opt ( keys( %$opts ) ) { # For each real option... |
| 104 | if ( not exists( $good{ $opt } ) ) { # Look its name in the set of known options... |
| 105 | $bad{ $opt } = 1; # Add unknown option to %bad set. |
| 106 | delete( $opts->{ $opt } ); # And delete original option. |
| 107 | }; # if |
| 108 | }; # foreach $opt |
| 109 | if ( %bad ) { # If %bad set is not empty... |
| 110 | my @caller = caller( 1 ); # Issue a warning. |
| 111 | local $Carp::CarpLevel = 2; |
| 112 | Carp::cluck( $caller[ 3 ] . ": " . $msg . ": " . join( ", ", sort( keys( %bad ) ) ) ); |
| 113 | }; # if |
| 114 | |
| 115 | return 1; |
| 116 | |
| 117 | }; # sub check_opts |
| 118 | |
| 119 | |
| 120 | # -------------------------------------------------------------------------------------------------- |
| 121 | # Purpose: |
| 122 | # Check subroutine arguments. |
| 123 | # Synopsis: |
| 124 | # my %opts = validate( params => \@_, spec => { ... }, caller => n ); |
| 125 | # Arguments: |
| 126 | # params -- A reference to subroutine's actual arguments. |
| 127 | # spec -- Specification of expected arguments. |
| 128 | # caller -- ... |
| 129 | # Return value: |
| 130 | # A hash of validated options. |
| 131 | # Description: |
| 132 | # I would like to use Params::Validate module, but it is not a part of default Perl |
| 133 | # distribution, so I cannot rely on it. This subroutine resembles to some extent to |
| 134 | # Params::Validate::validate_with(). |
| 135 | # Specification of expected arguments: |
| 136 | # { $opt => { type => $type, default => $default }, ... } |
| 137 | # $opt -- String, option name. |
| 138 | # $type -- String, expected type(s). Allowed values are "SCALAR", "UNDEF", "BOOLEAN", |
| 139 | # "ARRAYREF", "HASHREF", "CODEREF". Multiple types may listed using bar: |
| 140 | # "SCALAR|ARRAYREF". The type string is case-insensitive. |
| 141 | # $default -- Default value for an option. Will be used if option is not specified or |
| 142 | # undefined. |
| 143 | # |
| 144 | sub validate(@) { |
| 145 | |
| 146 | my %opts = @_; # Temporary use %opts for parameters of `validate' subroutine. |
| 147 | my $params = $opts{ params }; |
| 148 | my $caller = ( $opts{ caller } or 0 ) + 1; |
| 149 | my $spec = $opts{ spec }; |
| 150 | undef( %opts ); # Ok, Clean %opts, now we will collect result of the subroutine. |
| 151 | |
| 152 | # Find out caller package, filename, line, and subroutine name. |
| 153 | my ( $pkg, $file, $line, $subr ) = caller( $caller ); |
| 154 | my @errors; # We will collect errors in array not to stop on the first found error. |
| 155 | my $error = |
| 156 | sub ($) { |
| 157 | my $msg = shift( @_ ); |
| 158 | push( @errors, "$msg at $file line $line.\n" ); |
| 159 | }; # sub |
| 160 | |
| 161 | # Check options. |
| 162 | while ( @$params ) { |
| 163 | # Check option name. |
| 164 | my $opt = shift( @$params ); |
| 165 | if ( not exists( $spec->{ $opt } ) ) { |
| 166 | $error->( "Invalid option `$opt'" ); |
| 167 | shift( @$params ); # Skip value of unknow option. |
| 168 | next; |
| 169 | }; # if |
| 170 | # Check option value exists. |
| 171 | if ( not @$params ) { |
| 172 | $error->( "Option `$opt' does not have a value" ); |
| 173 | next; |
| 174 | }; # if |
| 175 | my $val = shift( @$params ); |
| 176 | # Check option value type. |
| 177 | if ( exists( $spec->{ $opt }->{ type } ) ) { |
| 178 | # Type specification exists. Check option value type. |
| 179 | my $actual_type; |
| 180 | if ( ref( $val ) ne "" ) { |
| 181 | $actual_type = ref( $val ) . "REF"; |
| 182 | } else { |
| 183 | $actual_type = ( defined( $val ) ? "SCALAR" : "UNDEF" ); |
| 184 | }; # if |
| 185 | my @wanted_types = split( m{\|}, lc( $spec->{ $opt }->{ type } ) ); |
| 186 | my $wanted_types = join( "|", map( $_ eq "boolean" ? "scalar|undef" : quotemeta( $_ ), @wanted_types ) ); |
| 187 | if ( $actual_type !~ m{\A(?:$wanted_types)\z}i ) { |
| 188 | $actual_type = lc( $actual_type ); |
| 189 | $wanted_types = lc( join( " or ", map( "`$_'", @wanted_types ) ) ); |
| 190 | $error->( "Option `$opt' value type is `$actual_type' but expected to be $wanted_types" ); |
| 191 | next; |
| 192 | }; # if |
| 193 | }; # if |
| 194 | if ( exists( $spec->{ $opt }->{ values } ) ) { |
| 195 | my $values = $spec->{ $opt }->{ values }; |
| 196 | if ( not grep( $_ eq $val, @$values ) ) { |
| 197 | $values = join( ", ", map( "`$_'", @$values ) ); |
| 198 | $error->( "Option `$opt' value is `$val' but expected to be one of $values" ); |
| 199 | next; |
| 200 | }; # if |
| 201 | }; # if |
| 202 | $opts{ $opt } = $val; |
| 203 | }; # while |
| 204 | |
| 205 | # Assign default values. |
| 206 | foreach my $opt ( keys( %$spec ) ) { |
| 207 | if ( not defined( $opts{ $opt } ) and exists( $spec->{ $opt }->{ default } ) ) { |
| 208 | $opts{ $opt } = $spec->{ $opt }->{ default }; |
| 209 | }; # if |
| 210 | }; # foreach $opt |
| 211 | |
| 212 | # If we found any errors, raise them. |
| 213 | if ( @errors ) { |
| 214 | die join( "", @errors ); |
| 215 | }; # if |
| 216 | |
| 217 | return %opts; |
| 218 | |
| 219 | }; # sub validate |
| 220 | |
| 221 | # ================================================================================================= |
| 222 | # Get option helpers. |
| 223 | # ================================================================================================= |
| 224 | |
| 225 | =head2 Get option helpers. |
| 226 | |
| 227 | =cut |
| 228 | |
| 229 | # ------------------------------------------------------------------------------------------------- |
| 230 | |
| 231 | =head3 get_options |
| 232 | |
| 233 | B<Synopsis:> |
| 234 | |
| 235 | get_options( @arguments ) |
| 236 | |
| 237 | B<Description:> |
| 238 | |
| 239 | It is very simple wrapper arounf Getopt::Long::GetOptions. It passes all arguments to GetOptions, |
| 240 | and add definitions for standard help options: --help, --doc, --verbose, and --quiet. |
| 241 | When GetOptions finihes, this subroutine checks exit code, if it is non-zero, standard error |
| 242 | message is issued and script terminated. |
| 243 | |
| 244 | If --verbose or --quiet option is specified, C<tools.pm_verbose> environment variable is set. |
| 245 | It is the way to propagate verbose/quiet mode to callee Perl scripts. |
| 246 | |
| 247 | =cut |
| 248 | |
| 249 | sub get_options { |
| 250 | |
| 251 | Getopt::Long::Configure( "no_ignore_case" ); |
| 252 | Getopt::Long::GetOptions( |
| 253 | "h0|usage" => sub { Pod::Usage::pod2usage( -exitval => 0, -verbose => 0 ); }, |
| 254 | "h1|h|help" => sub { Pod::Usage::pod2usage( -exitval => 0, -verbose => 1 ); }, |
| 255 | "h2|doc|manual" => sub { Pod::Usage::pod2usage( -exitval => 0, -verbose => 2 ); }, |
| 256 | "version" => sub { print( "$tool version $main::VERSION\n" ); exit( 0 ); }, |
| 257 | "v|verbose" => sub { ++ $verbose; $ENV{ "tools.pm_verbose" } = $verbose; }, |
| 258 | "quiet" => sub { -- $verbose; $ENV{ "tools.pm_verbose" } = $verbose; }, |
| 259 | "with-timestamps" => sub { $timestamps = 1; $ENV{ "tools.pm_timestamps" } = $timestamps; }, |
| 260 | @_, # Caller argumetsa are at the end so caller options overrides standard. |
| 261 | ) or cmdline_error(); |
| 262 | |
| 263 | }; # sub get_options |
| 264 | |
| 265 | |
| 266 | # ================================================================================================= |
| 267 | # Print utilities. |
| 268 | # ================================================================================================= |
| 269 | |
| 270 | =pod |
| 271 | |
| 272 | =head2 Print utilities. |
| 273 | |
| 274 | Each of the print subroutines prepends each line of its output with the name of current script and |
| 275 | the type of information, for example: |
| 276 | |
| 277 | info( "Writing file..." ); |
| 278 | |
| 279 | will print |
| 280 | |
| 281 | <script>: (i): Writing file... |
| 282 | |
| 283 | while |
| 284 | |
| 285 | warning( "File does not exist!" ); |
| 286 | |
| 287 | will print |
| 288 | |
| 289 | <script>: (!): File does not exist! |
| 290 | |
| 291 | Here are exported items: |
| 292 | |
| 293 | =cut |
| 294 | |
| 295 | # ------------------------------------------------------------------------------------------------- |
| 296 | |
| 297 | sub _format_message($\@;$) { |
| 298 | |
| 299 | my $prefix = shift( @_ ); |
| 300 | my $args = shift( @_ ); |
| 301 | my $no_eol = shift( @_ ); # Do not append "\n" to the last line. |
| 302 | my $message = ""; |
| 303 | |
| 304 | my $ts = ""; |
| 305 | if ( $timestamps ) { |
| 306 | my ( $sec, $min, $hour, $day, $month, $year ) = gmtime(); |
| 307 | $month += 1; |
| 308 | $year += 1900; |
| 309 | $ts = sprintf( "%04d-%02d-%02d %02d:%02d:%02d UTC: ", $year, $month, $day, $hour, $min, $sec ); |
| 310 | }; # if |
| 311 | for my $i ( 1 .. @$args ) { |
| 312 | my @lines = split( "\n", $args->[ $i - 1 ] ); |
| 313 | for my $j ( 1 .. @lines ) { |
| 314 | my $line = $lines[ $j - 1 ]; |
| 315 | my $last_line = ( ( $i == @$args ) and ( $j == @lines ) ); |
| 316 | my $eol = ( ( substr( $line, -1 ) eq "\n" ) or defined( $no_eol ) ? "" : "\n" ); |
| 317 | $message .= "$ts$tool: ($prefix) " . $line . $eol; |
| 318 | }; # foreach $j |
| 319 | }; # foreach $i |
| 320 | return $message; |
| 321 | |
| 322 | }; # sub _format_message |
| 323 | |
| 324 | #-------------------------------------------------------------------------------------------------- |
| 325 | |
| 326 | =pod |
| 327 | |
| 328 | =head3 $verbose |
| 329 | |
| 330 | B<Synopsis:> |
| 331 | |
| 332 | $verbose |
| 333 | |
| 334 | B<Description:> |
| 335 | |
| 336 | Package variable. It determines verbosity level, which affects C<warning()>, C<info()>, and |
| 337 | C<debug()> subroutnes . |
| 338 | |
| 339 | The variable gets initial value from C<tools.pm_verbose> environment variable if it is exists. |
| 340 | If the environment variable does not exist, variable is set to 2. |
| 341 | |
| 342 | Initial value may be overridden later directly or by C<get_options> function. |
| 343 | |
| 344 | =cut |
| 345 | |
| 346 | $verbose = exists( $ENV{ "tools.pm_verbose" } ) ? $ENV{ "tools.pm_verbose" } : 2; |
| 347 | |
| 348 | #-------------------------------------------------------------------------------------------------- |
| 349 | |
| 350 | =pod |
| 351 | |
| 352 | =head3 $timestamps |
| 353 | |
| 354 | B<Synopsis:> |
| 355 | |
| 356 | $timestamps |
| 357 | |
| 358 | B<Description:> |
| 359 | |
| 360 | Package variable. It determines whether C<debug()>, C<info()>, C<warning()>, C<runtime_error()> |
| 361 | subroutnes print timestamps or not. |
| 362 | |
| 363 | The variable gets initial value from C<tools.pm_timestamps> environment variable if it is exists. |
| 364 | If the environment variable does not exist, variable is set to false. |
| 365 | |
| 366 | Initial value may be overridden later directly or by C<get_options()> function. |
| 367 | |
| 368 | =cut |
| 369 | |
| 370 | $timestamps = exists( $ENV{ "tools.pm_timestamps" } ) ? $ENV{ "tools.pm_timestamps" } : 0; |
| 371 | |
| 372 | # ------------------------------------------------------------------------------------------------- |
| 373 | |
| 374 | =pod |
| 375 | |
| 376 | =head3 debug |
| 377 | |
| 378 | B<Synopsis:> |
| 379 | |
| 380 | debug( @messages ) |
| 381 | |
| 382 | B<Description:> |
| 383 | |
| 384 | If verbosity level is 3 or higher, print debug information to the stderr, prepending it with "(#)" |
| 385 | prefix. |
| 386 | |
| 387 | =cut |
| 388 | |
| 389 | sub debug(@) { |
| 390 | |
| 391 | if ( $verbose >= 3 ) { |
| 392 | STDOUT->flush(); |
| 393 | STDERR->print( _format_message( "#", @_ ) ); |
| 394 | }; # if |
| 395 | return 1; |
| 396 | |
| 397 | }; # sub debug |
| 398 | |
| 399 | #-------------------------------------------------------------------------------------------------- |
| 400 | |
| 401 | =pod |
| 402 | |
| 403 | =head3 info |
| 404 | |
| 405 | B<Synopsis:> |
| 406 | |
| 407 | info( @messages ) |
| 408 | |
| 409 | B<Description:> |
| 410 | |
| 411 | If verbosity level is 2 or higher, print information to the stderr, prepending it with "(i)" prefix. |
| 412 | |
| 413 | =cut |
| 414 | |
| 415 | sub info(@) { |
| 416 | |
| 417 | if ( $verbose >= 2 ) { |
| 418 | STDOUT->flush(); |
| 419 | STDERR->print( _format_message( "i", @_ ) ); |
| 420 | }; # if |
| 421 | |
| 422 | }; # sub info |
| 423 | |
| 424 | #-------------------------------------------------------------------------------------------------- |
| 425 | |
| 426 | =head3 warning |
| 427 | |
| 428 | B<Synopsis:> |
| 429 | |
| 430 | warning( @messages ) |
| 431 | |
| 432 | B<Description:> |
| 433 | |
| 434 | If verbosity level is 1 or higher, issue a warning, prepending it with "(!)" prefix. |
| 435 | |
| 436 | =cut |
| 437 | |
| 438 | sub warning(@) { |
| 439 | |
| 440 | if ( $verbose >= 1 ) { |
| 441 | STDOUT->flush(); |
| 442 | warn( _format_message( "!", @_ ) ); |
| 443 | }; # if |
| 444 | |
| 445 | }; # sub warning |
| 446 | |
| 447 | # ------------------------------------------------------------------------------------------------- |
| 448 | |
| 449 | =head3 cmdline_error |
| 450 | |
| 451 | B<Synopsis:> |
| 452 | |
| 453 | cmdline_error( @message ) |
| 454 | |
| 455 | B<Description:> |
| 456 | |
| 457 | Print error message and exit the program with status 2. |
| 458 | |
| 459 | This function is intended to complain on command line errors, e. g. unknown |
| 460 | options, invalid arguments, etc. |
| 461 | |
| 462 | =cut |
| 463 | |
| 464 | sub cmdline_error(;$) { |
| 465 | |
| 466 | my $message = shift( @_ ); |
| 467 | |
| 468 | if ( defined( $message ) ) { |
| 469 | if ( substr( $message, -1, 1 ) ne "\n" ) { |
| 470 | $message .= "\n"; |
| 471 | }; # if |
| 472 | } else { |
| 473 | $message = ""; |
| 474 | }; # if |
| 475 | STDOUT->flush(); |
| 476 | die $message . "Try --help option for more information.\n"; |
| 477 | |
| 478 | }; # sub cmdline_error |
| 479 | |
| 480 | # ------------------------------------------------------------------------------------------------- |
| 481 | |
| 482 | =head3 runtime_error |
| 483 | |
| 484 | B<Synopsis:> |
| 485 | |
| 486 | runtime_error( @message ) |
| 487 | |
| 488 | B<Description:> |
| 489 | |
| 490 | Print error message and exits the program with status 3. |
| 491 | |
| 492 | This function is intended to complain on runtime errors, e. g. |
| 493 | directories which are not found, non-writable files, etc. |
| 494 | |
| 495 | =cut |
| 496 | |
| 497 | sub runtime_error(@) { |
| 498 | |
| 499 | STDOUT->flush(); |
| 500 | die _format_message( "x", @_ ); |
| 501 | |
| 502 | }; # sub runtime_error |
| 503 | |
| 504 | #-------------------------------------------------------------------------------------------------- |
| 505 | |
| 506 | =head3 question |
| 507 | |
| 508 | B<Synopsis:> |
| 509 | |
| 510 | question( $prompt; $answer, $choices ) |
| 511 | |
| 512 | B<Description:> |
| 513 | |
| 514 | Print $promp to the stderr, prepending it with "question:" prefix. Read a line from stdin, chop |
| 515 | "\n" from the end, it is answer. |
| 516 | |
| 517 | If $answer is defined, it is treated as first user input. |
| 518 | |
| 519 | If $choices is specified, it could be a regexp for validating user input, or a string. In latter |
| 520 | case it interpreted as list of characters, acceptable (case-insensitive) choices. If user enters |
| 521 | non-acceptable answer, question continue asking until answer is acceptable. |
| 522 | If $choices is not specified, any answer is acceptable. |
| 523 | |
| 524 | In case of end-of-file (or Ctrl+D pressed by user), $answer is C<undef>. |
| 525 | |
| 526 | B<Examples:> |
| 527 | |
| 528 | my $answer; |
| 529 | question( "Save file [yn]? ", $answer, "yn" ); |
| 530 | # We accepts only "y", "Y", "n", or "N". |
| 531 | question( "Press enter to continue or Ctrl+C to abort..." ); |
| 532 | # We are not interested in answer value -- in case of Ctrl+C the script will be terminated, |
| 533 | # otherwise we continue execution. |
| 534 | question( "File name? ", $answer ); |
| 535 | # Any answer is acceptable. |
| 536 | |
| 537 | =cut |
| 538 | |
| 539 | sub question($;\$$) { |
| 540 | |
| 541 | my $prompt = shift( @_ ); |
| 542 | my $answer = shift( @_ ); |
| 543 | my $choices = shift( @_ ); |
| 544 | my $a = ( defined( $answer ) ? $$answer : undef ); |
| 545 | |
| 546 | if ( ref( $choices ) eq "Regexp" ) { |
Alp Toker | 8f2d3f0 | 2014-02-24 10:40:15 +0000 | [diff] [blame] | 547 | # It is already a regular expression, do nothing. |
Jim Cownie | 5e8470a | 2013-09-27 10:38:44 +0000 | [diff] [blame] | 548 | } elsif ( defined( $choices ) ) { |
| 549 | # Convert string to a regular expression. |
| 550 | $choices = qr/[@{ [ quotemeta( $choices ) ] }]/i; |
| 551 | }; # if |
| 552 | |
| 553 | for ( ; ; ) { |
| 554 | STDERR->print( _format_message( "?", @{ [ $prompt ] }, "no_eol" ) ); |
| 555 | STDERR->flush(); |
| 556 | if ( defined( $a ) ) { |
| 557 | STDOUT->print( $a . "\n" ); |
| 558 | } else { |
| 559 | $a = <STDIN>; |
| 560 | }; # if |
| 561 | if ( not defined( $a ) ) { |
| 562 | last; |
| 563 | }; # if |
| 564 | chomp( $a ); |
| 565 | if ( not defined( $choices ) or ( $a =~ m/^$choices$/ ) ) { |
| 566 | last; |
| 567 | }; # if |
| 568 | $a = undef; |
| 569 | }; # forever |
| 570 | if ( defined( $answer ) ) { |
| 571 | $$answer = $a; |
| 572 | }; # if |
| 573 | |
| 574 | }; # sub question |
| 575 | |
| 576 | # ------------------------------------------------------------------------------------------------- |
| 577 | |
| 578 | # Returns volume part of path. |
| 579 | sub get_vol($) { |
| 580 | |
| 581 | my $path = shift( @_ ); |
| 582 | my ( $vol, undef, undef ) = File::Spec->splitpath( $path ); |
| 583 | return $vol; |
| 584 | |
| 585 | }; # sub get_vol |
| 586 | |
| 587 | # Returns directory part of path. |
| 588 | sub get_dir($) { |
| 589 | |
| 590 | my $path = File::Spec->canonpath( shift( @_ ) ); |
| 591 | my ( $vol, $dir, undef ) = File::Spec->splitpath( $path ); |
| 592 | my @dirs = File::Spec->splitdir( $dir ); |
| 593 | pop( @dirs ); |
| 594 | $dir = File::Spec->catdir( @dirs ); |
| 595 | $dir = File::Spec->catpath( $vol, $dir, undef ); |
| 596 | return $dir; |
| 597 | |
| 598 | }; # sub get_dir |
| 599 | |
| 600 | # Returns file part of path. |
| 601 | sub get_file($) { |
| 602 | |
| 603 | my $path = shift( @_ ); |
| 604 | my ( undef, undef, $file ) = File::Spec->splitpath( $path ); |
| 605 | return $file; |
| 606 | |
| 607 | }; # sub get_file |
| 608 | |
| 609 | # Returns file part of path without last suffix. |
| 610 | sub get_name($) { |
| 611 | |
| 612 | my $path = shift( @_ ); |
| 613 | my ( undef, undef, $file ) = File::Spec->splitpath( $path ); |
| 614 | $file =~ s{\.[^.]*\z}{}; |
| 615 | return $file; |
| 616 | |
| 617 | }; # sub get_name |
| 618 | |
| 619 | # Returns last suffix of file part of path. |
| 620 | sub get_ext($) { |
| 621 | |
| 622 | my $path = shift( @_ ); |
| 623 | my ( undef, undef, $file ) = File::Spec->splitpath( $path ); |
| 624 | my $ext = ""; |
| 625 | if ( $file =~ m{(\.[^.]*)\z} ) { |
| 626 | $ext = $1; |
| 627 | }; # if |
| 628 | return $ext; |
| 629 | |
| 630 | }; # sub get_ext |
| 631 | |
| 632 | sub cat_file(@) { |
| 633 | |
| 634 | my $path = shift( @_ ); |
| 635 | my $file = pop( @_ ); |
| 636 | my @dirs = @_; |
| 637 | |
| 638 | my ( $vol, $dirs ) = File::Spec->splitpath( $path, "no_file" ); |
| 639 | @dirs = ( File::Spec->splitdir( $dirs ), @dirs ); |
| 640 | $dirs = File::Spec->catdir( @dirs ); |
| 641 | $path = File::Spec->catpath( $vol, $dirs, $file ); |
| 642 | |
| 643 | return $path; |
| 644 | |
| 645 | }; # sub cat_file |
| 646 | |
| 647 | sub cat_dir(@) { |
| 648 | |
| 649 | my $path = shift( @_ ); |
| 650 | my @dirs = @_; |
| 651 | |
| 652 | my ( $vol, $dirs ) = File::Spec->splitpath( $path, "no_file" ); |
| 653 | @dirs = ( File::Spec->splitdir( $dirs ), @dirs ); |
| 654 | $dirs = File::Spec->catdir( @dirs ); |
| 655 | $path = File::Spec->catpath( $vol, $dirs, "" ); |
| 656 | |
| 657 | return $path; |
| 658 | |
| 659 | }; # sub cat_dir |
| 660 | |
| 661 | # ================================================================================================= |
| 662 | # File and directory manipulation subroutines. |
| 663 | # ================================================================================================= |
| 664 | |
| 665 | =head2 File and directory manipulation subroutines. |
| 666 | |
| 667 | =over |
| 668 | |
| 669 | =cut |
| 670 | |
| 671 | # ------------------------------------------------------------------------------------------------- |
| 672 | |
| 673 | =item C<which( $file, @options )> |
| 674 | |
| 675 | Searches for specified executable file in the (specified) directories. |
| 676 | Raises a runtime eroror if no executable file found. Returns a full path of found executable(s). |
| 677 | |
| 678 | Options: |
| 679 | |
| 680 | =over |
| 681 | |
| 682 | =item C<-all> =E<gt> I<bool> |
| 683 | |
| 684 | Do not stop on the first found file. Note, that list of full paths is returned in this case. |
| 685 | |
| 686 | =item C<-dirs> =E<gt> I<ref_to_array> |
| 687 | |
| 688 | Specify directory list to search through. If option is not passed, PATH environment variable |
| 689 | is used for directory list. |
| 690 | |
| 691 | =item C<-exec> =E<gt> I<bool> |
| 692 | |
| 693 | Whether check for executable files or not. By default, C<which> searches executable files. |
| 694 | However, on Cygwin executable check never performed. |
| 695 | |
| 696 | =back |
| 697 | |
| 698 | Examples: |
| 699 | |
| 700 | Look for "echo" in the directories specified in PATH: |
| 701 | |
| 702 | my $echo = which( "echo" ); |
| 703 | |
| 704 | Look for all occurenses of "cp" in the PATH: |
| 705 | |
| 706 | my @cps = which( "cp", -all => 1 ); |
| 707 | |
Alp Toker | 8f2d3f0 | 2014-02-24 10:40:15 +0000 | [diff] [blame] | 708 | Look for the first occurrence of "icc" in the specified directories: |
Jim Cownie | 5e8470a | 2013-09-27 10:38:44 +0000 | [diff] [blame] | 709 | |
| 710 | my $icc = which( "icc", -dirs => [ ".", "/usr/local/bin", "/usr/bin", "/bin" ] ); |
| 711 | |
| 712 | Look for the the C<omp_lib.f> file: |
| 713 | |
| 714 | my @omp_lib = which( "omp_lib.f", -all => 1, -exec => 0, -dirs => [ @include ] ); |
| 715 | |
| 716 | =cut |
| 717 | |
| 718 | sub which($@) { |
| 719 | |
| 720 | my $file = shift( @_ ); |
| 721 | my %opts = @_; |
| 722 | |
| 723 | check_opts( %opts, [ qw( -all -dirs -exec ) ] ); |
| 724 | if ( $opts{ -all } and not wantarray() ) { |
| 725 | local $Carp::CarpLevel = 1; |
| 726 | Carp::cluck( "`-all' option passed to `which' but list is not expected" ); |
| 727 | }; # if |
| 728 | if ( not defined( $opts{ -exec } ) ) { |
| 729 | $opts{ -exec } = 1; |
| 730 | }; # if |
| 731 | |
| 732 | my $dirs = ( exists( $opts{ -dirs } ) ? $opts{ -dirs } : [ File::Spec->path() ] ); |
| 733 | my @found; |
| 734 | |
| 735 | my @exts = ( "" ); |
| 736 | if ( $^O eq "MSWin32" and $opts{ -exec } ) { |
| 737 | if ( defined( $ENV{ PATHEXT } ) ) { |
| 738 | push( @exts, split( ";", $ENV{ PATHEXT } ) ); |
| 739 | } else { |
| 740 | # If PATHEXT does not exist, use default value. |
| 741 | push( @exts, qw{ .COM .EXE .BAT .CMD } ); |
| 742 | }; # if |
| 743 | }; # if |
| 744 | |
| 745 | loop: |
| 746 | foreach my $dir ( @$dirs ) { |
| 747 | foreach my $ext ( @exts ) { |
| 748 | my $path = File::Spec->catfile( $dir, $file . $ext ); |
| 749 | if ( -e $path ) { |
| 750 | # Executable bit is not reliable on Cygwin, do not check it. |
| 751 | if ( not $opts{ -exec } or -x $path or $^O eq "cygwin" ) { |
| 752 | push( @found, $path ); |
| 753 | if ( not $opts{ -all } ) { |
| 754 | last loop; |
| 755 | }; # if |
| 756 | }; # if |
| 757 | }; # if |
| 758 | }; # foreach $ext |
| 759 | }; # foreach $dir |
| 760 | |
| 761 | if ( not @found ) { |
| 762 | # TBD: We need to introduce an option for conditional enabling this error. |
| 763 | # runtime_error( "Could not find \"$file\" executable file in PATH." ); |
| 764 | }; # if |
| 765 | if ( @found > 1 ) { |
| 766 | # TBD: Issue a warning? |
| 767 | }; # if |
| 768 | |
| 769 | if ( $opts{ -all } ) { |
| 770 | return @found; |
| 771 | } else { |
| 772 | return $found[ 0 ]; |
| 773 | }; # if |
| 774 | |
| 775 | }; # sub which |
| 776 | |
| 777 | # ------------------------------------------------------------------------------------------------- |
| 778 | |
| 779 | =item C<abs_path( $path, $base )> |
| 780 | |
| 781 | Return absolute path for an argument. |
| 782 | |
| 783 | Most of the work is done by C<File::Spec->rel2abs()>. C<abs_path()> additionally collapses |
| 784 | C<dir1/../dir2> to C<dir2>. |
| 785 | |
| 786 | It is not so naive and made intentionally. For example on Linux* OS in Bash if F<link/> is a symbolic |
| 787 | link to directory F<some_dir/> |
| 788 | |
| 789 | $ cd link |
| 790 | $ cd .. |
| 791 | |
| 792 | brings you back to F<link/>'s parent, not to parent of F<some_dir/>, |
| 793 | |
| 794 | =cut |
| 795 | |
| 796 | sub abs_path($;$) { |
| 797 | |
| 798 | my ( $path, $base ) = @_; |
| 799 | $path = File::Spec->rel2abs( $path, ( defined( $base ) ? $base : $ENV{ PWD } ) ); |
| 800 | my ( $vol, $dir, $file ) = File::Spec->splitpath( $path ); |
| 801 | while ( $dir =~ s{/(?!\.\.)[^/]*/\.\.(?:/|\z)}{/} ) { |
| 802 | }; # while |
| 803 | $path = File::Spec->canonpath( File::Spec->catpath( $vol, $dir, $file ) ); |
| 804 | return $path; |
| 805 | |
| 806 | }; # sub abs_path |
| 807 | |
| 808 | # ------------------------------------------------------------------------------------------------- |
| 809 | |
| 810 | =item C<rel_path( $path, $base )> |
| 811 | |
| 812 | Return relative path for an argument. |
| 813 | |
| 814 | =cut |
| 815 | |
| 816 | sub rel_path($;$) { |
| 817 | |
| 818 | my ( $path, $base ) = @_; |
| 819 | $path = File::Spec->abs2rel( abs_path( $path ), $base ); |
| 820 | return $path; |
| 821 | |
| 822 | }; # sub rel_path |
| 823 | |
| 824 | # ------------------------------------------------------------------------------------------------- |
| 825 | |
| 826 | =item C<real_path( $dir )> |
| 827 | |
| 828 | Return real absolute path for an argument. In the result all relative components (F<.> and F<..>) |
| 829 | and U<symbolic links are resolved>. |
| 830 | |
| 831 | In most cases it is not what you want. Consider using C<abs_path> first. |
| 832 | |
| 833 | C<abs_path> function from B<Cwd> module works with directories only. This function works with files |
| 834 | as well. But, if file is a symbolic link, function does not resolve it (yet). |
| 835 | |
| 836 | The function uses C<runtime_error> to raise an error if something wrong. |
| 837 | |
| 838 | =cut |
| 839 | |
| 840 | sub real_path($) { |
| 841 | |
| 842 | my $orig_path = shift( @_ ); |
| 843 | my $real_path; |
| 844 | my $message = ""; |
| 845 | if ( not -e $orig_path ) { |
| 846 | $message = "\"$orig_path\" does not exists"; |
| 847 | } else { |
| 848 | # Cwd::abs_path does not work with files, so in this case we should handle file separately. |
| 849 | my $file; |
| 850 | if ( not -d $orig_path ) { |
| 851 | ( my $vol, my $dir, $file ) = File::Spec->splitpath( File::Spec->rel2abs( $orig_path ) ); |
| 852 | $orig_path = File::Spec->catpath( $vol, $dir ); |
| 853 | }; # if |
| 854 | { |
| 855 | local $SIG{ __WARN__ } = sub { $message = $_[ 0 ]; }; |
| 856 | $real_path = Cwd::abs_path( $orig_path ); |
| 857 | }; |
| 858 | if ( defined( $file ) ) { |
| 859 | $real_path = File::Spec->catfile( $real_path, $file ); |
| 860 | }; # if |
| 861 | }; # if |
| 862 | if ( not defined( $real_path ) or $message ne "" ) { |
| 863 | $message =~ s/^stat\(.*\): (.*)\s+at .*? line \d+\s*\z/$1/; |
| 864 | runtime_error( "Could not find real path for \"$orig_path\"" . ( $message ne "" ? ": $message" : "" ) ); |
| 865 | }; # if |
| 866 | return $real_path; |
| 867 | |
| 868 | }; # sub real_path |
| 869 | |
| 870 | # ------------------------------------------------------------------------------------------------- |
| 871 | |
| 872 | =item C<make_dir( $dir, @options )> |
| 873 | |
| 874 | Make a directory. |
| 875 | |
| 876 | This function makes a directory. If necessary, more than one level can be created. |
| 877 | If directory exists, warning issues (the script behavior depends on value of |
| 878 | C<-warning_level> option). If directory creation fails or C<$dir> exists but it is not a |
| 879 | directory, error isssues. |
| 880 | |
| 881 | Options: |
| 882 | |
| 883 | =over |
| 884 | |
| 885 | =item C<-mode> |
| 886 | |
| 887 | The numeric mode for new directories, 0750 (rwxr-x---) by default. |
| 888 | |
| 889 | =back |
| 890 | |
| 891 | =cut |
| 892 | |
| 893 | sub make_dir($@) { |
| 894 | |
| 895 | my $dir = shift( @_ ); |
| 896 | my %opts = |
| 897 | validate( |
| 898 | params => \@_, |
| 899 | spec => { |
| 900 | parents => { type => "boolean", default => 1 }, |
| 901 | mode => { type => "scalar", default => 0777 }, |
| 902 | }, |
| 903 | ); |
| 904 | |
| 905 | my $prefix = "Could not create directory \"$dir\""; |
| 906 | |
| 907 | if ( -e $dir ) { |
| 908 | if ( -d $dir ) { |
| 909 | } else { |
| 910 | runtime_error( "$prefix: it exists, but not a directory." ); |
| 911 | }; # if |
| 912 | } else { |
| 913 | eval { |
| 914 | File::Path::mkpath( $dir, 0, $opts{ mode } ); |
| 915 | }; # eval |
| 916 | if ( $@ ) { |
| 917 | $@ =~ s{\s+at (?:[a-zA-Z0-9 /_.]*/)?tools\.pm line \d+\s*}{}; |
| 918 | runtime_error( "$prefix: $@" ); |
| 919 | }; # if |
| 920 | if ( not -d $dir ) { # Just in case, check it one more time... |
| 921 | runtime_error( "$prefix." ); |
| 922 | }; # if |
| 923 | }; # if |
| 924 | |
| 925 | }; # sub make_dir |
| 926 | |
| 927 | # ------------------------------------------------------------------------------------------------- |
| 928 | |
| 929 | =item C<copy_dir( $src_dir, $dst_dir, @options )> |
| 930 | |
| 931 | Copy directory recursively. |
| 932 | |
| 933 | This function copies a directory recursively. |
| 934 | If source directory does not exist or not a directory, error issues. |
| 935 | |
| 936 | Options: |
| 937 | |
| 938 | =over |
| 939 | |
| 940 | =item C<-overwrite> |
| 941 | |
| 942 | Overwrite destination directory, if it exists. |
| 943 | |
| 944 | =back |
| 945 | |
| 946 | =cut |
| 947 | |
| 948 | sub copy_dir($$@) { |
| 949 | |
| 950 | my $src = shift( @_ ); |
| 951 | my $dst = shift( @_ ); |
| 952 | my %opts = @_; |
| 953 | my $prefix = "Could not copy directory \"$src\" to \"$dst\""; |
| 954 | |
| 955 | if ( not -e $src ) { |
| 956 | runtime_error( "$prefix: \"$src\" does not exist." ); |
| 957 | }; # if |
| 958 | if ( not -d $src ) { |
| 959 | runtime_error( "$prefix: \"$src\" is not a directory." ); |
| 960 | }; # if |
| 961 | if ( -e $dst ) { |
| 962 | if ( -d $dst ) { |
| 963 | if ( $opts{ -overwrite } ) { |
| 964 | del_dir( $dst ); |
| 965 | } else { |
| 966 | runtime_error( "$prefix: \"$dst\" already exists." ); |
| 967 | }; # if |
| 968 | } else { |
| 969 | runtime_error( "$prefix: \"$dst\" is not a directory." ); |
| 970 | }; # if |
| 971 | }; # if |
| 972 | |
| 973 | execute( [ "cp", "-R", $src, $dst ] ); |
| 974 | |
| 975 | }; # sub copy_dir |
| 976 | |
| 977 | # ------------------------------------------------------------------------------------------------- |
| 978 | |
| 979 | =item C<move_dir( $src_dir, $dst_dir, @options )> |
| 980 | |
| 981 | Move directory. |
| 982 | |
| 983 | Options: |
| 984 | |
| 985 | =over |
| 986 | |
| 987 | =item C<-overwrite> |
| 988 | |
| 989 | Overwrite destination directory, if it exists. |
| 990 | |
| 991 | =back |
| 992 | |
| 993 | =cut |
| 994 | |
| 995 | sub move_dir($$@) { |
| 996 | |
| 997 | my $src = shift( @_ ); |
| 998 | my $dst = shift( @_ ); |
| 999 | my %opts = @_; |
| 1000 | my $prefix = "Could not copy directory \"$src\" to \"$dst\""; |
| 1001 | |
| 1002 | if ( not -e $src ) { |
| 1003 | runtime_error( "$prefix: \"$src\" does not exist." ); |
| 1004 | }; # if |
| 1005 | if ( not -d $src ) { |
| 1006 | runtime_error( "$prefix: \"$src\" is not a directory." ); |
| 1007 | }; # if |
| 1008 | if ( -e $dst ) { |
| 1009 | if ( -d $dst ) { |
| 1010 | if ( $opts{ -overwrite } ) { |
| 1011 | del_dir( $dst ); |
| 1012 | } else { |
| 1013 | runtime_error( "$prefix: \"$dst\" already exists." ); |
| 1014 | }; # if |
| 1015 | } else { |
| 1016 | runtime_error( "$prefix: \"$dst\" is not a directory." ); |
| 1017 | }; # if |
| 1018 | }; # if |
| 1019 | |
| 1020 | execute( [ "mv", $src, $dst ] ); |
| 1021 | |
| 1022 | }; # sub move_dir |
| 1023 | |
| 1024 | # ------------------------------------------------------------------------------------------------- |
| 1025 | |
| 1026 | =item C<clean_dir( $dir, @options )> |
| 1027 | |
| 1028 | Clean a directory: delete all the entries (recursively), but leave the directory. |
| 1029 | |
| 1030 | Options: |
| 1031 | |
| 1032 | =over |
| 1033 | |
| 1034 | =item C<-force> => bool |
| 1035 | |
| 1036 | If a directory is not writable, try to change permissions first, then clean it. |
| 1037 | |
| 1038 | =item C<-skip> => regexp |
| 1039 | |
| 1040 | Regexp. If a directory entry mached the regexp, it is skipped, not deleted. (As a subsequence, |
| 1041 | a directory containing skipped entries is not deleted.) |
| 1042 | |
| 1043 | =back |
| 1044 | |
| 1045 | =cut |
| 1046 | |
| 1047 | sub _clean_dir($); |
| 1048 | |
| 1049 | sub _clean_dir($) { |
| 1050 | our %_clean_dir_opts; |
| 1051 | my ( $dir ) = @_; |
| 1052 | my $skip = $_clean_dir_opts{ skip }; # Regexp. |
| 1053 | my $skipped = 0; # Number of skipped files. |
| 1054 | my $prefix = "Cleaning `$dir' failed:"; |
| 1055 | my @stat = stat( $dir ); |
| 1056 | my $mode = $stat[ 2 ]; |
| 1057 | if ( not @stat ) { |
| 1058 | runtime_error( $prefix, "Cannot stat `$dir': $!" ); |
| 1059 | }; # if |
| 1060 | if ( not -d _ ) { |
| 1061 | runtime_error( $prefix, "It is not a directory." ); |
| 1062 | }; # if |
| 1063 | if ( not -w _ ) { # Directory is not writable. |
| 1064 | if ( not -o _ or not $_clean_dir_opts{ force } ) { |
| 1065 | runtime_error( $prefix, "Directory is not writable." ); |
| 1066 | }; # if |
| 1067 | # Directory is not writable but mine. Try to change permissions. |
| 1068 | chmod( $mode | S_IWUSR, $dir ) |
| 1069 | or runtime_error( $prefix, "Cannot make directory writable: $!" ); |
| 1070 | }; # if |
| 1071 | my $handle = IO::Dir->new( $dir ) or runtime_error( $prefix, "Cannot read directory: $!" ); |
| 1072 | my @entries = File::Spec->no_upwards( $handle->read() ); |
| 1073 | $handle->close() or runtime_error( $prefix, "Cannot read directory: $!" ); |
| 1074 | foreach my $entry ( @entries ) { |
| 1075 | my $path = cat_file( $dir, $entry ); |
| 1076 | if ( defined( $skip ) and $entry =~ $skip ) { |
| 1077 | ++ $skipped; |
| 1078 | } else { |
| 1079 | if ( -l $path ) { |
| 1080 | unlink( $path ) or runtime_error( $prefix, "Cannot delete symlink `$path': $!" ); |
| 1081 | } else { |
| 1082 | stat( $path ) or runtime_error( $prefix, "Cannot stat `$path': $! " ); |
| 1083 | if ( -f _ ) { |
| 1084 | del_file( $path ); |
| 1085 | } elsif ( -d _ ) { |
| 1086 | my $rc = _clean_dir( $path ); |
| 1087 | if ( $rc == 0 ) { |
| 1088 | rmdir( $path ) or runtime_error( $prefix, "Cannot delete directory `$path': $!" ); |
| 1089 | }; # if |
| 1090 | $skipped += $rc; |
| 1091 | } else { |
| 1092 | runtime_error( $prefix, "`$path' is neither a file nor a directory." ); |
| 1093 | }; # if |
| 1094 | }; # if |
| 1095 | }; # if |
| 1096 | }; # foreach |
| 1097 | return $skipped; |
| 1098 | }; # sub _clean_dir |
| 1099 | |
| 1100 | |
| 1101 | sub clean_dir($@) { |
| 1102 | my $dir = shift( @_ ); |
| 1103 | our %_clean_dir_opts; |
| 1104 | local %_clean_dir_opts = |
| 1105 | validate( |
| 1106 | params => \@_, |
| 1107 | spec => { |
| 1108 | skip => { type => "regexpref" }, |
| 1109 | force => { type => "boolean" }, |
| 1110 | }, |
| 1111 | ); |
| 1112 | my $skipped = _clean_dir( $dir ); |
| 1113 | return $skipped; |
| 1114 | }; # sub clean_dir |
| 1115 | |
| 1116 | |
| 1117 | # ------------------------------------------------------------------------------------------------- |
| 1118 | |
| 1119 | =item C<del_dir( $dir, @options )> |
| 1120 | |
| 1121 | Delete a directory recursively. |
| 1122 | |
| 1123 | This function deletes a directory. If directory can not be deleted or it is not a directory, error |
| 1124 | message issues (and script exists). |
| 1125 | |
| 1126 | Options: |
| 1127 | |
| 1128 | =over |
| 1129 | |
| 1130 | =back |
| 1131 | |
| 1132 | =cut |
| 1133 | |
| 1134 | sub del_dir($@) { |
| 1135 | |
| 1136 | my $dir = shift( @_ ); |
| 1137 | my %opts = @_; |
| 1138 | my $prefix = "Deleting directory \"$dir\" failed"; |
| 1139 | our %_clean_dir_opts; |
| 1140 | local %_clean_dir_opts = |
| 1141 | validate( |
| 1142 | params => \@_, |
| 1143 | spec => { |
| 1144 | force => { type => "boolean" }, |
| 1145 | }, |
| 1146 | ); |
| 1147 | |
| 1148 | if ( not -e $dir ) { |
| 1149 | # Nothing to do. |
| 1150 | return; |
| 1151 | }; # if |
| 1152 | if ( not -d $dir ) { |
| 1153 | runtime_error( "$prefix: it is not a directory." ); |
| 1154 | }; # if |
| 1155 | _clean_dir( $dir ); |
| 1156 | rmdir( $dir ) or runtime_error( "$prefix." ); |
| 1157 | |
| 1158 | }; # sub del_dir |
| 1159 | |
| 1160 | # ------------------------------------------------------------------------------------------------- |
| 1161 | |
| 1162 | =item C<change_dir( $dir )> |
| 1163 | |
| 1164 | Change current directory. |
| 1165 | |
Alp Toker | 8f2d3f0 | 2014-02-24 10:40:15 +0000 | [diff] [blame] | 1166 | If any error occurred, error issues and script exits. |
Jim Cownie | 5e8470a | 2013-09-27 10:38:44 +0000 | [diff] [blame] | 1167 | |
| 1168 | =cut |
| 1169 | |
| 1170 | sub change_dir($) { |
| 1171 | |
| 1172 | my $dir = shift( @_ ); |
| 1173 | |
| 1174 | Cwd::chdir( $dir ) |
| 1175 | or runtime_error( "Could not chdir to \"$dir\": $!" ); |
| 1176 | |
| 1177 | }; # sub change_dir |
| 1178 | |
| 1179 | |
| 1180 | # ------------------------------------------------------------------------------------------------- |
| 1181 | |
| 1182 | =item C<copy_file( $src_file, $dst_file, @options )> |
| 1183 | |
| 1184 | Copy file. |
| 1185 | |
| 1186 | This function copies a file. If source does not exist or is not a file, error issues. |
| 1187 | |
| 1188 | Options: |
| 1189 | |
| 1190 | =over |
| 1191 | |
| 1192 | =item C<-overwrite> |
| 1193 | |
| 1194 | Overwrite destination file, if it exists. |
| 1195 | |
| 1196 | =back |
| 1197 | |
| 1198 | =cut |
| 1199 | |
| 1200 | sub copy_file($$@) { |
| 1201 | |
| 1202 | my $src = shift( @_ ); |
| 1203 | my $dst = shift( @_ ); |
| 1204 | my %opts = @_; |
| 1205 | my $prefix = "Could not copy file \"$src\" to \"$dst\""; |
| 1206 | |
| 1207 | if ( not -e $src ) { |
| 1208 | runtime_error( "$prefix: \"$src\" does not exist." ); |
| 1209 | }; # if |
| 1210 | if ( not -f $src ) { |
| 1211 | runtime_error( "$prefix: \"$src\" is not a file." ); |
| 1212 | }; # if |
| 1213 | if ( -e $dst ) { |
| 1214 | if ( -f $dst ) { |
| 1215 | if ( $opts{ -overwrite } ) { |
| 1216 | del_file( $dst ); |
| 1217 | } else { |
| 1218 | runtime_error( "$prefix: \"$dst\" already exists." ); |
| 1219 | }; # if |
| 1220 | } else { |
| 1221 | runtime_error( "$prefix: \"$dst\" is not a file." ); |
| 1222 | }; # if |
| 1223 | }; # if |
| 1224 | |
| 1225 | File::Copy::copy( $src, $dst ) or runtime_error( "$prefix: $!" ); |
| 1226 | # On Windows* OS File::Copy preserves file attributes, but on Linux* OS it doesn't. |
| 1227 | # So we should do it manually... |
| 1228 | if ( $^O =~ m/^linux\z/ ) { |
| 1229 | my $mode = ( stat( $src ) )[ 2 ] |
| 1230 | or runtime_error( "$prefix: cannot get status info for source file." ); |
| 1231 | chmod( $mode, $dst ) |
| 1232 | or runtime_error( "$prefix: cannot change mode of destination file." ); |
| 1233 | }; # if |
| 1234 | |
| 1235 | }; # sub copy_file |
| 1236 | |
| 1237 | # ------------------------------------------------------------------------------------------------- |
| 1238 | |
| 1239 | sub move_file($$@) { |
| 1240 | |
| 1241 | my $src = shift( @_ ); |
| 1242 | my $dst = shift( @_ ); |
| 1243 | my %opts = @_; |
| 1244 | my $prefix = "Could not move file \"$src\" to \"$dst\""; |
| 1245 | |
| 1246 | check_opts( %opts, [ qw( -overwrite ) ] ); |
| 1247 | |
| 1248 | if ( not -e $src ) { |
| 1249 | runtime_error( "$prefix: \"$src\" does not exist." ); |
| 1250 | }; # if |
| 1251 | if ( not -f $src ) { |
| 1252 | runtime_error( "$prefix: \"$src\" is not a file." ); |
| 1253 | }; # if |
| 1254 | if ( -e $dst ) { |
| 1255 | if ( -f $dst ) { |
| 1256 | if ( $opts{ -overwrite } ) { |
| 1257 | # |
| 1258 | } else { |
| 1259 | runtime_error( "$prefix: \"$dst\" already exists." ); |
| 1260 | }; # if |
| 1261 | } else { |
| 1262 | runtime_error( "$prefix: \"$dst\" is not a file." ); |
| 1263 | }; # if |
| 1264 | }; # if |
| 1265 | |
| 1266 | File::Copy::move( $src, $dst ) or runtime_error( "$prefix: $!" ); |
| 1267 | |
| 1268 | }; # sub move_file |
| 1269 | |
| 1270 | # ------------------------------------------------------------------------------------------------- |
| 1271 | |
| 1272 | sub del_file($) { |
| 1273 | my $files = shift( @_ ); |
| 1274 | if ( ref( $files ) eq "" ) { |
| 1275 | $files = [ $files ]; |
| 1276 | }; # if |
| 1277 | foreach my $file ( @$files ) { |
| 1278 | debug( "Deleting file `$file'..." ); |
| 1279 | my $rc = unlink( $file ); |
| 1280 | if ( $rc == 0 && $! != ENOENT ) { |
| 1281 | # Reporn an error, but ignore ENOENT, because the goal is achieved. |
| 1282 | runtime_error( "Deleting file `$file' failed: $!" ); |
| 1283 | }; # if |
| 1284 | }; # foreach $file |
| 1285 | }; # sub del_file |
| 1286 | |
| 1287 | # ------------------------------------------------------------------------------------------------- |
| 1288 | |
| 1289 | =back |
| 1290 | |
| 1291 | =cut |
| 1292 | |
| 1293 | # ================================================================================================= |
| 1294 | # File I/O subroutines. |
| 1295 | # ================================================================================================= |
| 1296 | |
| 1297 | =head2 File I/O subroutines. |
| 1298 | |
| 1299 | =cut |
| 1300 | |
| 1301 | #-------------------------------------------------------------------------------------------------- |
| 1302 | |
| 1303 | =head3 read_file |
| 1304 | |
| 1305 | B<Synopsis:> |
| 1306 | |
| 1307 | read_file( $file, @options ) |
| 1308 | |
| 1309 | B<Description:> |
| 1310 | |
| 1311 | Read file and return its content. In scalar context function returns a scalar, in list context |
| 1312 | function returns list of lines. |
| 1313 | |
| 1314 | Note: If the last of file does not terminate with newline, function will append it. |
| 1315 | |
| 1316 | B<Arguments:> |
| 1317 | |
| 1318 | =over |
| 1319 | |
| 1320 | =item B<$file> |
| 1321 | |
| 1322 | A name or handle of file to read from. |
| 1323 | |
| 1324 | =back |
| 1325 | |
| 1326 | B<Options:> |
| 1327 | |
| 1328 | =over |
| 1329 | |
| 1330 | =item B<-binary> |
| 1331 | |
| 1332 | If true, file treats as a binary file: no newline conversion, no truncating trailing space, no |
| 1333 | newline removing performed. Entire file returned as a scalar. |
| 1334 | |
| 1335 | =item B<-bulk> |
| 1336 | |
| 1337 | This option is allowed only in binary mode. Option's value should be a reference to a scalar. |
| 1338 | If option present, file content placed to pointee scalar and function returns true (1). |
| 1339 | |
| 1340 | =item B<-chomp> |
| 1341 | |
| 1342 | If true, newline characters are removed from file content. By default newline characters remain. |
| 1343 | This option is not applicable in binary mode. |
| 1344 | |
| 1345 | =item B<-keep_trailing_space> |
| 1346 | |
| 1347 | If true, trainling space remain at the ends of lines. By default all trailing spaces are removed. |
| 1348 | This option is not applicable in binary mode. |
| 1349 | |
| 1350 | =back |
| 1351 | |
| 1352 | B<Examples:> |
| 1353 | |
| 1354 | Return file as single line, remove trailing spaces. |
| 1355 | |
| 1356 | my $bulk = read_file( "message.txt" ); |
| 1357 | |
| 1358 | Return file as list of lines with removed trailing space and |
| 1359 | newline characters. |
| 1360 | |
| 1361 | my @bulk = read_file( "message.txt", -chomp => 1 ); |
| 1362 | |
| 1363 | Read a binary file: |
| 1364 | |
| 1365 | my $bulk = read_file( "message.txt", -binary => 1 ); |
| 1366 | |
| 1367 | Read a big binary file: |
| 1368 | |
| 1369 | my $bulk; |
| 1370 | read_file( "big_binary_file", -binary => 1, -bulk => \$bulk ); |
| 1371 | |
| 1372 | Read from standard input: |
| 1373 | |
| 1374 | my @bulk = read_file( \*STDIN ); |
| 1375 | |
| 1376 | =cut |
| 1377 | |
| 1378 | sub read_file($@) { |
| 1379 | |
| 1380 | my $file = shift( @_ ); # The name or handle of file to read from. |
| 1381 | my %opts = @_; # Options. |
| 1382 | |
| 1383 | my $name; |
| 1384 | my $handle; |
| 1385 | my @bulk; |
| 1386 | my $error = \&runtime_error; |
| 1387 | |
| 1388 | my @binopts = qw( -binary -error -bulk ); # Options available in binary mode. |
| 1389 | my @txtopts = qw( -binary -error -keep_trailing_space -chomp -layer ); # Options available in text (non-binary) mode. |
| 1390 | check_opts( %opts, [ @binopts, @txtopts ] ); |
| 1391 | if ( $opts{ -binary } ) { |
| 1392 | check_opts( %opts, [ @binopts ], "these options cannot be used with -binary" ); |
| 1393 | } else { |
| 1394 | check_opts( %opts, [ @txtopts ], "these options cannot be used without -binary" ); |
| 1395 | }; # if |
| 1396 | if ( not exists( $opts{ -error } ) ) { |
| 1397 | $opts{ -error } = "error"; |
| 1398 | }; # if |
| 1399 | if ( $opts{ -error } eq "warning" ) { |
| 1400 | $error = \&warning; |
| 1401 | } elsif( $opts{ -error } eq "ignore" ) { |
| 1402 | $error = sub {}; |
| 1403 | } elsif ( ref( $opts{ -error } ) eq "ARRAY" ) { |
| 1404 | $error = sub { push( @{ $opts{ -error } }, $_[ 0 ] ); }; |
| 1405 | }; # if |
| 1406 | |
| 1407 | if ( ( ref( $file ) eq "GLOB" ) or UNIVERSAL::isa( $file, "IO::Handle" ) ) { |
| 1408 | $name = "unknown"; |
| 1409 | $handle = $file; |
| 1410 | } else { |
| 1411 | $name = $file; |
| 1412 | if ( get_ext( $file ) eq ".gz" and not $opts{ -binary } ) { |
| 1413 | $handle = IO::Zlib->new( $name, "rb" ); |
| 1414 | } else { |
| 1415 | $handle = IO::File->new( $name, "r" ); |
| 1416 | }; # if |
| 1417 | if ( not defined( $handle ) ) { |
| 1418 | $error->( "File \"$name\" could not be opened for input: $!" ); |
| 1419 | }; # if |
| 1420 | }; # if |
| 1421 | if ( defined( $handle ) ) { |
| 1422 | if ( $opts{ -binary } ) { |
| 1423 | binmode( $handle ); |
| 1424 | local $/ = undef; # Set input record separator to undef to read entire file as one line. |
| 1425 | if ( exists( $opts{ -bulk } ) ) { |
| 1426 | ${ $opts{ -bulk } } = $handle->getline(); |
| 1427 | } else { |
| 1428 | $bulk[ 0 ] = $handle->getline(); |
| 1429 | }; # if |
| 1430 | } else { |
| 1431 | if ( defined( $opts{ -layer } ) ) { |
| 1432 | binmode( $handle, $opts{ -layer } ); |
| 1433 | }; # if |
| 1434 | @bulk = $handle->getlines(); |
| 1435 | # Special trick for UTF-8 files: Delete BOM, if any. |
| 1436 | if ( defined( $opts{ -layer } ) and $opts{ -layer } eq ":utf8" ) { |
| 1437 | if ( substr( $bulk[ 0 ], 0, 1 ) eq "\x{FEFF}" ) { |
| 1438 | substr( $bulk[ 0 ], 0, 1 ) = ""; |
| 1439 | }; # if |
| 1440 | }; # if |
| 1441 | }; # if |
| 1442 | $handle->close() |
| 1443 | or $error->( "File \"$name\" could not be closed after input: $!" ); |
| 1444 | } else { |
| 1445 | if ( $opts{ -binary } and exists( $opts{ -bulk } ) ) { |
| 1446 | ${ $opts{ -bulk } } = ""; |
| 1447 | }; # if |
| 1448 | }; # if |
| 1449 | if ( $opts{ -binary } ) { |
| 1450 | if ( exists( $opts{ -bulk } ) ) { |
| 1451 | return 1; |
| 1452 | } else { |
| 1453 | return $bulk[ 0 ]; |
| 1454 | }; # if |
| 1455 | } else { |
| 1456 | if ( ( @bulk > 0 ) and ( substr( $bulk[ -1 ], -1, 1 ) ne "\n" ) ) { |
| 1457 | $bulk[ -1 ] .= "\n"; |
| 1458 | }; # if |
| 1459 | if ( not $opts{ -keep_trailing_space } ) { |
| 1460 | map( $_ =~ s/\s+\n\z/\n/, @bulk ); |
| 1461 | }; # if |
| 1462 | if ( $opts{ -chomp } ) { |
| 1463 | chomp( @bulk ); |
| 1464 | }; # if |
| 1465 | if ( wantarray() ) { |
| 1466 | return @bulk; |
| 1467 | } else { |
| 1468 | return join( "", @bulk ); |
| 1469 | }; # if |
| 1470 | }; # if |
| 1471 | |
| 1472 | }; # sub read_file |
| 1473 | |
| 1474 | #-------------------------------------------------------------------------------------------------- |
| 1475 | |
| 1476 | =head3 write_file |
| 1477 | |
| 1478 | B<Synopsis:> |
| 1479 | |
| 1480 | write_file( $file, $bulk, @options ) |
| 1481 | |
| 1482 | B<Description:> |
| 1483 | |
| 1484 | Write file. |
| 1485 | |
| 1486 | B<Arguments:> |
| 1487 | |
| 1488 | =over |
| 1489 | |
| 1490 | =item B<$file> |
| 1491 | |
| 1492 | The name or handle of file to writte to. |
| 1493 | |
| 1494 | =item B<$bulk> |
| 1495 | |
| 1496 | Bulk to write to a file. Can be a scalar, or a reference to scalar or an array. |
| 1497 | |
| 1498 | =back |
| 1499 | |
| 1500 | B<Options:> |
| 1501 | |
| 1502 | =over |
| 1503 | |
| 1504 | =item B<-backup> |
| 1505 | |
| 1506 | If true, create a backup copy of file overwritten. Backup copy is placed into the same directory. |
| 1507 | The name of backup copy is the same as the name of file with `~' appended. By default backup copy |
| 1508 | is not created. |
| 1509 | |
| 1510 | =item B<-append> |
| 1511 | |
| 1512 | If true, the text will be added to existing file. |
| 1513 | |
| 1514 | =back |
| 1515 | |
| 1516 | B<Examples:> |
| 1517 | |
| 1518 | write_file( "message.txt", \$bulk ); |
| 1519 | # Write file, take content from a scalar. |
| 1520 | |
| 1521 | write_file( "message.txt", \@bulk, -backup => 1 ); |
| 1522 | # Write file, take content from an array, create a backup copy. |
| 1523 | |
| 1524 | =cut |
| 1525 | |
| 1526 | sub write_file($$@) { |
| 1527 | |
| 1528 | my $file = shift( @_ ); # The name or handle of file to write to. |
| 1529 | my $bulk = shift( @_ ); # The text to write. Can be reference to array or scalar. |
| 1530 | my %opts = @_; # Options. |
| 1531 | |
| 1532 | my $name; |
| 1533 | my $handle; |
| 1534 | |
| 1535 | check_opts( %opts, [ qw( -append -backup -binary -layer ) ] ); |
| 1536 | |
| 1537 | my $mode = $opts{ -append } ? "a": "w"; |
| 1538 | if ( ( ref( $file ) eq "GLOB" ) or UNIVERSAL::isa( $file, "IO::Handle" ) ) { |
| 1539 | $name = "unknown"; |
| 1540 | $handle = $file; |
| 1541 | } else { |
| 1542 | $name = $file; |
| 1543 | if ( $opts{ -backup } and ( -f $name ) ) { |
| 1544 | copy_file( $name, $name . "~", -overwrite => 1 ); |
| 1545 | }; # if |
| 1546 | $handle = IO::File->new( $name, $mode ) |
| 1547 | or runtime_error( "File \"$name\" could not be opened for output: $!" ); |
| 1548 | }; # if |
| 1549 | if ( $opts{ -binary } ) { |
| 1550 | binmode( $handle ); |
| 1551 | } elsif ( $opts{ -layer } ) { |
| 1552 | binmode( $handle, $opts{ -layer } ); |
| 1553 | }; # if |
| 1554 | if ( ref( $bulk ) eq "" ) { |
| 1555 | if ( defined( $bulk ) ) { |
| 1556 | $handle->print( $bulk ); |
| 1557 | if ( not $opts{ -binary } and ( substr( $bulk, -1 ) ne "\n" ) ) { |
| 1558 | $handle->print( "\n" ); |
| 1559 | }; # if |
| 1560 | }; # if |
| 1561 | } elsif ( ref( $bulk ) eq "SCALAR" ) { |
| 1562 | if ( defined( $$bulk ) ) { |
| 1563 | $handle->print( $$bulk ); |
| 1564 | if ( not $opts{ -binary } and ( substr( $$bulk, -1 ) ne "\n" ) ) { |
| 1565 | $handle->print( "\n" ); |
| 1566 | }; # if |
| 1567 | }; # if |
| 1568 | } elsif ( ref( $bulk ) eq "ARRAY" ) { |
| 1569 | foreach my $line ( @$bulk ) { |
| 1570 | if ( defined( $line ) ) { |
| 1571 | $handle->print( $line ); |
| 1572 | if ( not $opts{ -binary } and ( substr( $line, -1 ) ne "\n" ) ) { |
| 1573 | $handle->print( "\n" ); |
| 1574 | }; # if |
| 1575 | }; # if |
| 1576 | }; # foreach |
| 1577 | } else { |
| 1578 | Carp::croak( "write_file: \$bulk must be a scalar or reference to (scalar or array)" ); |
| 1579 | }; # if |
| 1580 | $handle->close() |
| 1581 | or runtime_error( "File \"$name\" could not be closed after output: $!" ); |
| 1582 | |
| 1583 | }; # sub write_file |
| 1584 | |
| 1585 | #-------------------------------------------------------------------------------------------------- |
| 1586 | |
| 1587 | =cut |
| 1588 | |
| 1589 | # ================================================================================================= |
| 1590 | # Execution subroutines. |
| 1591 | # ================================================================================================= |
| 1592 | |
| 1593 | =head2 Execution subroutines. |
| 1594 | |
| 1595 | =over |
| 1596 | |
| 1597 | =cut |
| 1598 | |
| 1599 | #-------------------------------------------------------------------------------------------------- |
| 1600 | |
| 1601 | sub _pre { |
| 1602 | |
| 1603 | my $arg = shift( @_ ); |
| 1604 | |
| 1605 | # If redirection is not required, exit. |
| 1606 | if ( not exists( $arg->{ redir } ) ) { |
| 1607 | return 0; |
| 1608 | }; # if |
| 1609 | |
| 1610 | # Input parameters. |
| 1611 | my $mode = $arg->{ mode }; # Mode, "<" (input ) or ">" (output). |
| 1612 | my $handle = $arg->{ handle }; # Handle to manipulate. |
| 1613 | my $redir = $arg->{ redir }; # Data, a file name if a scalar, or file contents, if a reference. |
| 1614 | |
| 1615 | # Output parameters. |
| 1616 | my $save_handle; |
| 1617 | my $temp_handle; |
| 1618 | my $temp_name; |
| 1619 | |
| 1620 | # Save original handle (by duping it). |
| 1621 | $save_handle = Symbol::gensym(); |
| 1622 | $handle->flush(); |
| 1623 | open( $save_handle, $mode . "&" . $handle->fileno() ) |
| 1624 | or die( "Cannot dup filehandle: $!" ); |
| 1625 | |
| 1626 | # Prepare a file to IO. |
| 1627 | if ( UNIVERSAL::isa( $redir, "IO::Handle" ) or ( ref( $redir ) eq "GLOB" ) ) { |
| 1628 | # $redir is reference to an object of IO::Handle class (or its decedant). |
| 1629 | $temp_handle = $redir; |
| 1630 | } elsif ( ref( $redir ) ) { |
| 1631 | # $redir is a reference to content to be read/written. |
| 1632 | # Prepare temp file. |
| 1633 | ( $temp_handle, $temp_name ) = |
| 1634 | File::Temp::tempfile( |
| 1635 | "$tool.XXXXXXXX", |
| 1636 | DIR => File::Spec->tmpdir(), |
| 1637 | SUFFIX => ".tmp", |
| 1638 | UNLINK => 1 |
| 1639 | ); |
| 1640 | if ( not defined( $temp_handle ) ) { |
| 1641 | runtime_error( "Could not create temp file." ); |
| 1642 | }; # if |
| 1643 | if ( $mode eq "<" ) { |
| 1644 | # It is a file to be read by child, prepare file content to be read. |
| 1645 | $temp_handle->print( ref( $redir ) eq "SCALAR" ? ${ $redir } : @{ $redir } ); |
| 1646 | $temp_handle->flush(); |
| 1647 | seek( $temp_handle, 0, 0 ); |
| 1648 | # Unfortunatelly, I could not use OO interface to seek. |
| 1649 | # ActivePerl 5.6.1 complains on both forms: |
| 1650 | # $temp_handle->seek( 0 ); # As declared in IO::Seekable. |
| 1651 | # $temp_handle->setpos( 0 ); # As described in documentation. |
| 1652 | } elsif ( $mode eq ">" ) { |
| 1653 | # It is a file for output. Clear output variable. |
| 1654 | if ( ref( $redir ) eq "SCALAR" ) { |
| 1655 | ${ $redir } = ""; |
| 1656 | } else { |
| 1657 | @{ $redir } = (); |
| 1658 | }; # if |
| 1659 | }; # if |
| 1660 | } else { |
| 1661 | # $redir is a name of file to be read/written. |
| 1662 | # Just open file. |
| 1663 | if ( defined( $redir ) ) { |
| 1664 | $temp_name = $redir; |
| 1665 | } else { |
| 1666 | $temp_name = File::Spec->devnull(); |
| 1667 | }; # if |
| 1668 | $temp_handle = IO::File->new( $temp_name, $mode ) |
| 1669 | or runtime_error( "file \"$temp_name\" could not be opened for " . ( $mode eq "<" ? "input" : "output" ) . ": $!" ); |
| 1670 | }; # if |
| 1671 | |
| 1672 | # Redirect handle to temp file. |
| 1673 | open( $handle, $mode . "&" . $temp_handle->fileno() ) |
| 1674 | or die( "Cannot dup filehandle: $!" ); |
| 1675 | |
| 1676 | # Save output parameters. |
| 1677 | $arg->{ save_handle } = $save_handle; |
| 1678 | $arg->{ temp_handle } = $temp_handle; |
| 1679 | $arg->{ temp_name } = $temp_name; |
| 1680 | |
| 1681 | }; # sub _pre |
| 1682 | |
| 1683 | |
| 1684 | sub _post { |
| 1685 | |
| 1686 | my $arg = shift( @_ ); |
| 1687 | |
| 1688 | # Input parameters. |
| 1689 | my $mode = $arg->{ mode }; # Mode, "<" or ">". |
| 1690 | my $handle = $arg->{ handle }; # Handle to save and set. |
| 1691 | my $redir = $arg->{ redir }; # Data, a file name if a scalar, or file contents, if a reference. |
| 1692 | |
| 1693 | # Parameters saved during preprocessing. |
| 1694 | my $save_handle = $arg->{ save_handle }; |
| 1695 | my $temp_handle = $arg->{ temp_handle }; |
| 1696 | my $temp_name = $arg->{ temp_name }; |
| 1697 | |
| 1698 | # If no handle was saved, exit. |
| 1699 | if ( not $save_handle ) { |
| 1700 | return 0; |
| 1701 | }; # if |
| 1702 | |
| 1703 | # Close handle. |
| 1704 | $handle->close() |
| 1705 | or die( "$!" ); |
| 1706 | |
| 1707 | # Read the content of temp file, if necessary, and close temp file. |
| 1708 | if ( ( $mode ne "<" ) and ref( $redir ) ) { |
| 1709 | $temp_handle->flush(); |
| 1710 | seek( $temp_handle, 0, 0 ); |
| 1711 | if ( $^O =~ m/MSWin/ ) { |
| 1712 | binmode( $temp_handle, ":crlf" ); |
| 1713 | }; # if |
| 1714 | if ( ref( $redir ) eq "SCALAR" ) { |
| 1715 | ${ $redir } .= join( "", $temp_handle->getlines() ); |
| 1716 | } elsif ( ref( $redir ) eq "ARRAY" ) { |
| 1717 | push( @{ $redir }, $temp_handle->getlines() ); |
| 1718 | }; # if |
| 1719 | }; # if |
| 1720 | if ( not UNIVERSAL::isa( $redir, "IO::Handle" ) ) { |
| 1721 | $temp_handle->close() |
| 1722 | or die( "$!" ); |
| 1723 | }; # if |
| 1724 | |
| 1725 | # Restore handle to original value. |
| 1726 | $save_handle->flush(); |
| 1727 | open( $handle, $mode . "&" . $save_handle->fileno() ) |
| 1728 | or die( "Cannot dup filehandle: $!" ); |
| 1729 | |
| 1730 | # Close save handle. |
| 1731 | $save_handle->close() |
| 1732 | or die( "$!" ); |
| 1733 | |
| 1734 | # Delete parameters saved during preprocessing. |
| 1735 | delete( $arg->{ save_handle } ); |
| 1736 | delete( $arg->{ temp_handle } ); |
| 1737 | delete( $arg->{ temp_name } ); |
| 1738 | |
| 1739 | }; # sub _post |
| 1740 | |
| 1741 | #-------------------------------------------------------------------------------------------------- |
| 1742 | |
| 1743 | =item C<execute( [ @command ], @options )> |
| 1744 | |
| 1745 | Execute specified program or shell command. |
| 1746 | |
| 1747 | Program is specified by reference to an array, that array is passed to C<system()> function which |
| 1748 | executes the command. See L<perlfunc> for details how C<system()> interprets various forms of |
| 1749 | C<@command>. |
| 1750 | |
| 1751 | By default, in case of any error error message is issued and script terminated (by runtime_error()). |
| 1752 | Function returns an exit code of program. |
| 1753 | |
| 1754 | Alternatively, he function may return exit status of the program (see C<-ignore_status>) or signal |
| 1755 | (see C<-ignore_signal>) so caller may analyze it and continue execution. |
| 1756 | |
| 1757 | Options: |
| 1758 | |
| 1759 | =over |
| 1760 | |
| 1761 | =item C<-stdin> |
| 1762 | |
| 1763 | Redirect stdin of program. The value of option can be: |
| 1764 | |
| 1765 | =over |
| 1766 | |
| 1767 | =item C<undef> |
| 1768 | |
| 1769 | Stdin of child is attached to null device. |
| 1770 | |
| 1771 | =item a string |
| 1772 | |
| 1773 | Stdin of child is attached to a file with name specified by option. |
| 1774 | |
| 1775 | =item a reference to a scalar |
| 1776 | |
| 1777 | A dereferenced scalar is written to a temp file, and child's stdin is attached to that file. |
| 1778 | |
| 1779 | =item a reference to an array |
| 1780 | |
| 1781 | A dereferenced array is written to a temp file, and child's stdin is attached to that file. |
| 1782 | |
| 1783 | =back |
| 1784 | |
| 1785 | =item C<-stdout> |
| 1786 | |
| 1787 | Redirect stdout. Possible values are the same as for C<-stdin> option. The only difference is |
| 1788 | reference specifies a variable receiving program's output. |
| 1789 | |
| 1790 | =item C<-stderr> |
| 1791 | |
| 1792 | It similar to C<-stdout>, but redirects stderr. There is only one additional value: |
| 1793 | |
| 1794 | =over |
| 1795 | |
| 1796 | =item an empty string |
| 1797 | |
| 1798 | means that stderr should be redirected to the same place where stdout is redirected to. |
| 1799 | |
| 1800 | =back |
| 1801 | |
| 1802 | =item C<-append> |
| 1803 | |
| 1804 | Redirected stream will not overwrite previous content of file (or variable). |
| 1805 | Note, that option affects both stdout and stderr. |
| 1806 | |
| 1807 | =item C<-ignore_status> |
| 1808 | |
| 1809 | By default, subroutine raises an error and exits the script if program returns non-exit status. If |
| 1810 | this options is true, no error is raised. Instead, status is returned as function result (and $@ is |
| 1811 | set to error message). |
| 1812 | |
| 1813 | =item C<-ignore_signal> |
| 1814 | |
| 1815 | By default, subroutine raises an error and exits the script if program die with signal. If |
| 1816 | this options is true, no error is raised in such a case. Instead, signal number is returned (as |
| 1817 | negative value), error message is placed to C<$@> variable. |
| 1818 | |
| 1819 | If command is not even started, -256 is returned. |
| 1820 | |
| 1821 | =back |
| 1822 | |
| 1823 | Examples: |
| 1824 | |
| 1825 | execute( [ "cmd.exe", "/c", "dir" ] ); |
| 1826 | # Execute NT shell with specified options, no redirections are |
| 1827 | # made. |
| 1828 | |
| 1829 | my $output; |
| 1830 | execute( [ "cvs", "-n", "-q", "update", "." ], -stdout => \$output ); |
| 1831 | # Execute "cvs -n -q update ." command, output is saved |
| 1832 | # in $output variable. |
| 1833 | |
| 1834 | my @output; |
| 1835 | execute( [ qw( cvs -n -q update . ) ], -stdout => \@output, -stderr => undef ); |
| 1836 | # Execute specified command, output is saved in @output |
| 1837 | # variable, stderr stream is redirected to null device |
| 1838 | # (/dev/null in Linux* OS an nul in Windows* OS). |
| 1839 | |
| 1840 | =cut |
| 1841 | |
| 1842 | sub execute($@) { |
| 1843 | |
| 1844 | # !!! Add something to complain on unknown options... |
| 1845 | |
| 1846 | my $command = shift( @_ ); |
| 1847 | my %opts = @_; |
| 1848 | my $prefix = "Could not execute $command->[ 0 ]"; |
| 1849 | |
| 1850 | check_opts( %opts, [ qw( -stdin -stdout -stderr -append -ignore_status -ignore_signal ) ] ); |
| 1851 | |
| 1852 | if ( ref( $command ) ne "ARRAY" ) { |
| 1853 | Carp::croak( "execute: $command must be a reference to array" ); |
| 1854 | }; # if |
| 1855 | |
| 1856 | my $stdin = { handle => \*STDIN, mode => "<" }; |
| 1857 | my $stdout = { handle => \*STDOUT, mode => ">" }; |
| 1858 | my $stderr = { handle => \*STDERR, mode => ">" }; |
| 1859 | my $streams = { |
| 1860 | stdin => $stdin, |
| 1861 | stdout => $stdout, |
| 1862 | stderr => $stderr |
| 1863 | }; # $streams |
| 1864 | |
| 1865 | for my $stream ( qw( stdin stdout stderr ) ) { |
| 1866 | if ( exists( $opts{ "-$stream" } ) ) { |
| 1867 | if ( ref( $opts{ "-$stream" } ) !~ m/\A(|SCALAR|ARRAY)\z/ ) { |
| 1868 | Carp::croak( "execute: -$stream option: must have value of scalar, or reference to (scalar or array)." ); |
| 1869 | }; # if |
| 1870 | $streams->{ $stream }->{ redir } = $opts{ "-$stream" }; |
| 1871 | }; # if |
| 1872 | if ( $opts{ -append } and ( $streams->{ $stream }->{ mode } ) eq ">" ) { |
| 1873 | $streams->{ $stream }->{ mode } = ">>"; |
| 1874 | }; # if |
| 1875 | }; # foreach $stream |
| 1876 | |
| 1877 | _pre( $stdin ); |
| 1878 | _pre( $stdout ); |
| 1879 | if ( defined( $stderr->{ redir } ) and not ref( $stderr->{ redir } ) and ( $stderr->{ redir } eq "" ) ) { |
| 1880 | if ( exists( $stdout->{ redir } ) ) { |
| 1881 | $stderr->{ redir } = $stdout->{ temp_handle }; |
| 1882 | } else { |
| 1883 | $stderr->{ redir } = ${ $stdout->{ handle } }; |
| 1884 | }; # if |
| 1885 | }; # if |
| 1886 | _pre( $stderr ); |
| 1887 | my $rc = system( @$command ); |
| 1888 | my $errno = $!; |
| 1889 | my $child = $?; |
| 1890 | _post( $stderr ); |
| 1891 | _post( $stdout ); |
| 1892 | _post( $stdin ); |
| 1893 | |
| 1894 | my $exit = 0; |
| 1895 | my $signal_num = $child & 127; |
| 1896 | my $exit_status = $child >> 8; |
| 1897 | $@ = ""; |
| 1898 | |
| 1899 | if ( $rc == -1 ) { |
| 1900 | $@ = "\"$command->[ 0 ]\" failed: $errno"; |
| 1901 | $exit = -256; |
| 1902 | if ( not $opts{ -ignore_signal } ) { |
| 1903 | runtime_error( $@ ); |
| 1904 | }; # if |
| 1905 | } elsif ( $signal_num != 0 ) { |
| 1906 | $@ = "\"$command->[ 0 ]\" failed due to signal $signal_num."; |
| 1907 | $exit = - $signal_num; |
| 1908 | if ( not $opts{ -ignore_signal } ) { |
| 1909 | runtime_error( $@ ); |
| 1910 | }; # if |
| 1911 | } elsif ( $exit_status != 0 ) { |
| 1912 | $@ = "\"$command->[ 0 ]\" returned non-zero status $exit_status."; |
| 1913 | $exit = $exit_status; |
| 1914 | if ( not $opts{ -ignore_status } ) { |
| 1915 | runtime_error( $@ ); |
| 1916 | }; # if |
| 1917 | }; # if |
| 1918 | |
| 1919 | return $exit; |
| 1920 | |
| 1921 | }; # sub execute |
| 1922 | |
| 1923 | #-------------------------------------------------------------------------------------------------- |
| 1924 | |
| 1925 | =item C<backticks( [ @command ], @options )> |
| 1926 | |
| 1927 | Run specified program or shell command and return output. |
| 1928 | |
| 1929 | In scalar context entire output is returned in a single string. In list context list of strings |
| 1930 | is returned. Function issues an error and exits script if any error occurs. |
| 1931 | |
| 1932 | =cut |
| 1933 | |
| 1934 | |
| 1935 | sub backticks($@) { |
| 1936 | |
| 1937 | my $command = shift( @_ ); |
| 1938 | my %opts = @_; |
| 1939 | my @output; |
| 1940 | |
| 1941 | check_opts( %opts, [ qw( -chomp ) ] ); |
| 1942 | |
| 1943 | execute( $command, -stdout => \@output ); |
| 1944 | |
| 1945 | if ( $opts{ -chomp } ) { |
| 1946 | chomp( @output ); |
| 1947 | }; # if |
| 1948 | |
| 1949 | return ( wantarray() ? @output : join( "", @output ) ); |
| 1950 | |
| 1951 | }; # sub backticks |
| 1952 | |
| 1953 | #-------------------------------------------------------------------------------------------------- |
| 1954 | |
| 1955 | sub pad($$$) { |
| 1956 | my ( $str, $length, $pad ) = @_; |
| 1957 | my $lstr = length( $str ); # Length of source string. |
| 1958 | if ( $lstr < $length ) { |
| 1959 | my $lpad = length( $pad ); # Length of pad. |
| 1960 | my $count = int( ( $length - $lstr ) / $lpad ); # Number of pad repetitions. |
| 1961 | my $tail = $length - ( $lstr + $lpad * $count ); |
| 1962 | $str = $str . ( $pad x $count ) . substr( $pad, 0, $tail ); |
| 1963 | }; # if |
| 1964 | return $str; |
| 1965 | }; # sub pad |
| 1966 | |
| 1967 | # -------------------------------------------------------------------------------------------------- |
| 1968 | |
| 1969 | =back |
| 1970 | |
| 1971 | =cut |
| 1972 | |
| 1973 | #-------------------------------------------------------------------------------------------------- |
| 1974 | |
| 1975 | return 1; |
| 1976 | |
| 1977 | #-------------------------------------------------------------------------------------------------- |
| 1978 | |
| 1979 | =cut |
| 1980 | |
| 1981 | # End of file. |