| line | stmt | bran | cond | sub | pod | time | code | 
| 1 | 1 |  |  | 1 |  | 15932 | use strict; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 35 |  | 
| 2 | 1 |  |  | 1 |  | 4 | use warnings; | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 55 |  | 
| 3 |  |  |  |  |  |  |  | 
| 4 |  |  |  |  |  |  | package IPC::Open3::Callback::Command; | 
| 5 |  |  |  |  |  |  | $IPC::Open3::Callback::Command::VERSION = '1.16'; | 
| 6 |  |  |  |  |  |  | # ABSTRACT: A utility class that provides subroutines for building shell command strings. | 
| 7 |  |  |  |  |  |  |  | 
| 8 | 1 |  |  | 1 |  | 3 | use Exporter qw(import); | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 46 |  | 
| 9 |  |  |  |  |  |  | our @EXPORT_OK = | 
| 10 |  |  |  |  |  |  | qw(batch_command command command_options cp_command mkdir_command pipe_command rm_command sed_command write_command); | 
| 11 |  |  |  |  |  |  |  | 
| 12 | 1 |  |  | 1 |  | 4 | use File::Spec; | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 1472 |  | 
| 13 |  |  |  |  |  |  |  | 
| 14 |  |  |  |  |  |  | sub batch_command { | 
| 15 | 0 |  |  | 0 |  |  | my ( @commands, $batch_options, $command_options ); | 
| 16 | 0 |  |  |  |  |  | (@commands) = @_; | 
| 17 | 0 | 0 |  |  |  |  | $command_options = pop(@commands) | 
| 18 |  |  |  |  |  |  | if ( ref( $commands[$#commands] ) eq 'IPC::Open3::Callback::Command::CommandOptions' ); | 
| 19 | 0 | 0 |  |  |  |  | $batch_options = pop(@commands) if ( ref( $commands[$#commands] ) eq 'HASH' ); | 
| 20 |  |  |  |  |  |  |  | 
| 21 | 0 | 0 |  |  |  |  | push( @commands, $command_options ) if ($command_options); | 
| 22 |  |  |  |  |  |  |  | 
| 23 |  |  |  |  |  |  | wrap( | 
| 24 |  |  |  |  |  |  | $batch_options || {}, | 
| 25 |  |  |  |  |  |  | @commands, | 
| 26 |  |  |  |  |  |  | sub { | 
| 27 | 0 |  |  | 0 |  |  | return @_; | 
| 28 |  |  |  |  |  |  | } | 
| 29 | 0 |  | 0 |  |  |  | ); | 
| 30 |  |  |  |  |  |  | } | 
| 31 |  |  |  |  |  |  |  | 
| 32 |  |  |  |  |  |  | sub command_options { | 
| 33 | 0 |  |  | 0 |  |  | return IPC::Open3::Callback::Command::CommandOptions->new(@_); | 
| 34 |  |  |  |  |  |  | } | 
| 35 |  |  |  |  |  |  |  | 
| 36 |  |  |  |  |  |  | sub command { | 
| 37 |  |  |  |  |  |  | wrap( | 
| 38 |  |  |  |  |  |  | {}, | 
| 39 |  |  |  |  |  |  | @_, | 
| 40 |  |  |  |  |  |  | sub { | 
| 41 | 0 |  |  | 0 |  |  | return shift; | 
| 42 |  |  |  |  |  |  | } | 
| 43 | 0 |  |  | 0 |  |  | ); | 
| 44 |  |  |  |  |  |  | } | 
| 45 |  |  |  |  |  |  |  | 
| 46 |  |  |  |  |  |  | sub cp_command { | 
| 47 | 0 |  |  | 0 |  |  | my ( $source_path, $source_command_options, $destination_path, $destination_command_options, | 
| 48 |  |  |  |  |  |  | %cp_options ); | 
| 49 | 0 |  |  |  |  |  | $source_path            = shift; | 
| 50 | 0 | 0 |  |  |  |  | $source_command_options = shift | 
| 51 |  |  |  |  |  |  | if ( ref( $_[0] ) eq 'IPC::Open3::Callback::Command::CommandOptions' ); | 
| 52 | 0 |  |  |  |  |  | $destination_path            = shift; | 
| 53 | 0 | 0 |  |  |  |  | $destination_command_options = shift | 
| 54 |  |  |  |  |  |  | if ( ref( $_[0] ) eq 'IPC::Open3::Callback::Command::CommandOptions' ); | 
| 55 | 0 |  |  |  |  |  | %cp_options = @_; | 
| 56 |  |  |  |  |  |  |  | 
| 57 | 0 | 0 |  |  |  |  | $source_command_options      = command_options() if ( !$source_command_options ); | 
| 58 | 0 | 0 |  |  |  |  | $destination_command_options = command_options() if ( !$destination_command_options ); | 
| 59 |  |  |  |  |  |  |  | 
| 60 | 0 |  |  |  |  |  | my $source_command; | 
| 61 |  |  |  |  |  |  | my $destination_command; | 
| 62 | 0 | 0 |  |  |  |  | if ( $cp_options{file} ) { | 
| 63 |  |  |  |  |  |  |  | 
| 64 |  |  |  |  |  |  | # is a file, so use cat | dd | 
| 65 | 0 | 0 |  |  |  |  | if ( $cp_options{compress} ) { | 
| 66 | 0 |  |  |  |  |  | $source_command = command( "gzip -c $source_path", $source_command_options ); | 
| 67 | 0 |  |  |  |  |  | $destination_command = | 
| 68 |  |  |  |  |  |  | pipe_command( "gunzip", "dd of=$destination_path", $destination_command_options ); | 
| 69 |  |  |  |  |  |  | } | 
| 70 |  |  |  |  |  |  | else { | 
| 71 | 0 |  |  |  |  |  | $source_command = command( "cat $source_path", $source_command_options ); | 
| 72 | 0 |  |  |  |  |  | $destination_command = | 
| 73 |  |  |  |  |  |  | command( "dd of=$destination_path", $destination_command_options ); | 
| 74 |  |  |  |  |  |  | } | 
| 75 |  |  |  |  |  |  | } | 
| 76 |  |  |  |  |  |  | else { | 
| 77 |  |  |  |  |  |  | # is a directory, so use tar or unzip | 
| 78 | 0 | 0 | 0 |  |  |  | if ( $cp_options{archive} && $cp_options{archive} eq 'zip' ) { | 
| 79 | 0 |  | 0 |  |  |  | my $temp_zip = File::Spec->catfile( $destination_path, | 
| 80 |  |  |  |  |  |  | $cp_options{unzip_temp_file} || "temp_cp_command.zip" ); | 
| 81 | 0 |  |  |  |  |  | $source_command = | 
| 82 |  |  |  |  |  |  | command( | 
| 83 |  |  |  |  |  |  | batch_command( "cd $source_path", "zip -qr - .", { subshell => "bash -c " } ), | 
| 84 |  |  |  |  |  |  | $source_command_options ); | 
| 85 | 0 |  |  |  |  |  | $destination_command = batch_command( | 
| 86 |  |  |  |  |  |  | "dd of=$temp_zip",     "unzip -qod $destination_path $temp_zip", | 
| 87 |  |  |  |  |  |  | rm_command($temp_zip), $destination_command_options | 
| 88 |  |  |  |  |  |  | ); | 
| 89 |  |  |  |  |  |  | } | 
| 90 |  |  |  |  |  |  | else { | 
| 91 |  |  |  |  |  |  | # default, use tar | 
| 92 | 0 |  |  |  |  |  | my @parts             = ("tar c -C $source_path ."); | 
| 93 | 0 |  |  |  |  |  | my @destination_parts = (); | 
| 94 | 0 | 0 |  |  |  |  | if ( $cp_options{status} ) { | 
| 95 | 0 |  | 0 |  |  |  | push( | 
| 96 |  |  |  |  |  |  | @parts, | 
| 97 |  |  |  |  |  |  | join( | 
| 98 |  |  |  |  |  |  | '', | 
| 99 |  |  |  |  |  |  | 'pv -f -s `', | 
| 100 |  |  |  |  |  |  | _sudo_command( | 
| 101 |  |  |  |  |  |  | $source_command_options | 
| 102 |  |  |  |  |  |  | && $source_command_options->get_sudo_username(), | 
| 103 |  |  |  |  |  |  | pipe_command( "du -sb $source_path", 'cut -f1' ) | 
| 104 |  |  |  |  |  |  | ), | 
| 105 |  |  |  |  |  |  | '`' | 
| 106 |  |  |  |  |  |  | ) | 
| 107 |  |  |  |  |  |  | ); | 
| 108 |  |  |  |  |  |  | } | 
| 109 | 0 | 0 |  |  |  |  | if ( $cp_options{compress} ) { | 
| 110 | 0 |  |  |  |  |  | push( @parts,             'gzip' ); | 
| 111 | 0 |  |  |  |  |  | push( @destination_parts, 'gunzip' ); | 
| 112 |  |  |  |  |  |  | } | 
| 113 |  |  |  |  |  |  | push( | 
| 114 | 0 |  | 0 |  |  |  | @destination_parts, | 
| 115 |  |  |  |  |  |  | _sudo_command( | 
| 116 |  |  |  |  |  |  | $destination_command_options | 
| 117 |  |  |  |  |  |  | && $destination_command_options->get_sudo_username(), | 
| 118 |  |  |  |  |  |  | "tar x -C $destination_path" | 
| 119 |  |  |  |  |  |  | ) | 
| 120 |  |  |  |  |  |  | ); | 
| 121 |  |  |  |  |  |  |  | 
| 122 | 0 |  |  |  |  |  | $source_command = command( pipe_command(@parts), $source_command_options ); | 
| 123 | 0 |  |  |  |  |  | $destination_command = command( pipe_command(@destination_parts), | 
| 124 |  |  |  |  |  |  | $destination_command_options->clone( sudo_username => undef ) ); | 
| 125 |  |  |  |  |  |  | } | 
| 126 |  |  |  |  |  |  | } | 
| 127 |  |  |  |  |  |  |  | 
| 128 | 0 |  |  |  |  |  | return pipe_command( $source_command, $destination_command ); | 
| 129 |  |  |  |  |  |  | } | 
| 130 |  |  |  |  |  |  |  | 
| 131 |  |  |  |  |  |  | sub mkdir_command { | 
| 132 |  |  |  |  |  |  | wrap( | 
| 133 |  |  |  |  |  |  | {}, | 
| 134 |  |  |  |  |  |  | @_, | 
| 135 |  |  |  |  |  |  | sub { | 
| 136 | 0 |  |  | 0 |  |  | return 'mkdir -p "' . join( '" "', @_ ) . '"'; | 
| 137 |  |  |  |  |  |  | } | 
| 138 | 0 |  |  | 0 |  |  | ); | 
| 139 |  |  |  |  |  |  | } | 
| 140 |  |  |  |  |  |  |  | 
| 141 |  |  |  |  |  |  | sub pipe_command { | 
| 142 |  |  |  |  |  |  | wrap( | 
| 143 |  |  |  |  |  |  | { command_separator => '|' }, | 
| 144 |  |  |  |  |  |  | @_, | 
| 145 |  |  |  |  |  |  | sub { | 
| 146 | 0 |  |  | 0 |  |  | return @_; | 
| 147 |  |  |  |  |  |  | } | 
| 148 | 0 |  |  | 0 |  |  | ); | 
| 149 |  |  |  |  |  |  | } | 
| 150 |  |  |  |  |  |  |  | 
| 151 |  |  |  |  |  |  | sub _quote_command { | 
| 152 | 0 |  |  | 0 |  |  | my ($command) = @_; | 
| 153 | 0 |  |  |  |  |  | $command =~ s/\\/\\\\/g; | 
| 154 | 0 |  |  |  |  |  | $command =~ s/`/\\`/g;    # for `command` | 
| 155 | 0 |  |  |  |  |  | $command =~ s/"/\\"/g; | 
| 156 | 0 |  |  |  |  |  | return "\"$command\""; | 
| 157 |  |  |  |  |  |  | } | 
| 158 |  |  |  |  |  |  |  | 
| 159 |  |  |  |  |  |  | sub rm_command { | 
| 160 |  |  |  |  |  |  | wrap( | 
| 161 |  |  |  |  |  |  | {}, | 
| 162 |  |  |  |  |  |  | @_, | 
| 163 |  |  |  |  |  |  | sub { | 
| 164 | 0 |  |  | 0 |  |  | return 'rm -rf "' . join( '" "', @_ ) . '"'; | 
| 165 |  |  |  |  |  |  | } | 
| 166 | 0 |  |  | 0 |  |  | ); | 
| 167 |  |  |  |  |  |  | } | 
| 168 |  |  |  |  |  |  |  | 
| 169 |  |  |  |  |  |  | sub sed_command { | 
| 170 |  |  |  |  |  |  | wrap( | 
| 171 |  |  |  |  |  |  | {}, | 
| 172 |  |  |  |  |  |  | @_, | 
| 173 |  |  |  |  |  |  | sub { | 
| 174 | 0 |  |  | 0 |  |  | my @args    = @_; | 
| 175 | 0 |  |  |  |  |  | my $options = {}; | 
| 176 |  |  |  |  |  |  |  | 
| 177 | 0 | 0 |  |  |  |  | if ( ref( $args[$#args] ) eq 'HASH' ) { | 
| 178 | 0 |  |  |  |  |  | $options = pop(@args); | 
| 179 |  |  |  |  |  |  | } | 
| 180 |  |  |  |  |  |  |  | 
| 181 | 0 |  |  |  |  |  | my $command = 'sed'; | 
| 182 | 0 | 0 |  |  |  |  | $command .= ' -i' if ( $options->{in_place} ); | 
| 183 | 0 | 0 |  |  |  |  | if ( defined( $options->{temp_script_file} ) ) { | 
| 184 | 0 |  |  |  |  |  | my $temp_script_file_name = $options->{temp_script_file}->filename(); | 
| 185 | 0 | 0 |  |  |  |  | print( { $options->{temp_script_file} } join( ' ', '', map {"$_;"} @args ) ) | 
|  | 0 |  |  |  |  |  |  | 
|  | 0 |  |  |  |  |  |  | 
| 186 |  |  |  |  |  |  | if ( scalar(@args) ); | 
| 187 | 0 |  |  |  |  |  | print( | 
| 188 | 0 |  |  |  |  |  | { $options->{temp_script_file} } join( ' ', | 
| 189 |  |  |  |  |  |  | '', | 
| 190 | 0 |  |  |  |  |  | map {"s/$_/$options->{replace_map}{$_}/g;"} | 
| 191 | 0 | 0 |  |  |  |  | keys( %{ $options->{replace_map} } ) ) | 
| 192 |  |  |  |  |  |  | ) if ( defined( $options->{replace_map} ) ); | 
| 193 | 0 |  |  |  |  |  | $options->{temp_script_file}->flush(); | 
| 194 | 0 |  |  |  |  |  | $command .= " -f $temp_script_file_name"; | 
| 195 |  |  |  |  |  |  | } | 
| 196 |  |  |  |  |  |  | else { | 
| 197 | 0 | 0 |  |  |  |  | $command .= join( ' ', '', map {"-e '$_'"} @args ) if ( scalar(@args) ); | 
|  | 0 |  |  |  |  |  |  | 
| 198 | 0 |  |  |  |  |  | $command .= join( ' ', | 
| 199 |  |  |  |  |  |  | '', | 
| 200 | 0 |  |  |  |  |  | map {"-e 's/$_/$options->{replace_map}{$_}/g'"} | 
| 201 | 0 | 0 |  |  |  |  | keys( %{ $options->{replace_map} } ) ) | 
| 202 |  |  |  |  |  |  | if ( defined( $options->{replace_map} ) ); | 
| 203 |  |  |  |  |  |  | } | 
| 204 | 0 | 0 |  |  |  |  | $command .= join( ' ', '', @{ $options->{files} } ) if ( $options->{files} ); | 
|  | 0 |  |  |  |  |  |  | 
| 205 |  |  |  |  |  |  |  | 
| 206 | 0 |  |  |  |  |  | return $command; | 
| 207 |  |  |  |  |  |  | } | 
| 208 | 0 |  |  | 0 |  |  | ); | 
| 209 |  |  |  |  |  |  | } | 
| 210 |  |  |  |  |  |  |  | 
| 211 |  |  |  |  |  |  | sub _sudo_command { | 
| 212 | 0 |  |  | 0 |  |  | my ( $sudo_username, $command ) = @_; | 
| 213 | 0 | 0 |  |  |  |  | if ( defined($sudo_username) ) { | 
| 214 | 0 | 0 |  |  |  |  | $command = "sudo " . ( $sudo_username ? "-u $sudo_username " : '' ) . $command; | 
| 215 |  |  |  |  |  |  | } | 
| 216 | 0 |  |  |  |  |  | return $command; | 
| 217 |  |  |  |  |  |  | } | 
| 218 |  |  |  |  |  |  |  | 
| 219 |  |  |  |  |  |  | sub write_command { | 
| 220 | 0 |  |  | 0 |  |  | my ( $filename, @lines, $write_options, $command_options ); | 
| 221 | 0 |  |  |  |  |  | $filename        = shift; | 
| 222 | 0 |  |  |  |  |  | @lines           = @_; | 
| 223 | 0 | 0 |  |  |  |  | $command_options = pop(@lines) | 
| 224 |  |  |  |  |  |  | if ( ref( $lines[$#lines] ) eq 'IPC::Open3::Callback::Command::CommandOptions' ); | 
| 225 | 0 | 0 |  |  |  |  | $write_options = pop(@lines) if ( ref( $lines[$#lines] ) eq 'HASH' ); | 
| 226 |  |  |  |  |  |  |  | 
| 227 | 0 |  |  |  |  |  | my $remote_command = "dd of=$filename"; | 
| 228 | 0 | 0 | 0 |  |  |  | if ( defined($write_options) && defined( $write_options->{mode} ) ) { | 
|  |  | 0 |  |  |  |  |  | 
| 229 | 0 | 0 |  |  |  |  | if ( defined($command_options) ) { | 
|  |  | 0 |  |  |  |  |  | 
| 230 | 0 |  |  |  |  |  | $remote_command = | 
| 231 |  |  |  |  |  |  | batch_command( $remote_command, "chmod $write_options->{mode} $filename", | 
| 232 |  |  |  |  |  |  | $command_options ); | 
| 233 |  |  |  |  |  |  | } | 
| 234 |  |  |  |  |  |  | elsif ( defined($command_options) ) { | 
| 235 | 0 |  |  |  |  |  | $remote_command = | 
| 236 |  |  |  |  |  |  | batch_command( $remote_command, "chmod $write_options->{mode} $filename" ); | 
| 237 |  |  |  |  |  |  | } | 
| 238 |  |  |  |  |  |  | } | 
| 239 |  |  |  |  |  |  | elsif ( defined($command_options) ) { | 
| 240 | 0 |  |  |  |  |  | $remote_command = command( $remote_command, $command_options ); | 
| 241 |  |  |  |  |  |  | } | 
| 242 |  |  |  |  |  |  |  | 
| 243 | 0 | 0 | 0 |  |  |  | my $line_separator = | 
| 244 |  |  |  |  |  |  | ( defined($write_options) && defined( $write_options->{line_separator} ) ) | 
| 245 |  |  |  |  |  |  | ? $write_options->{line_separator} | 
| 246 |  |  |  |  |  |  | : '\n'; | 
| 247 | 0 |  |  |  |  |  | return pipe_command( 'printf "' . join( $line_separator, @lines ) . '"', $remote_command ); | 
| 248 |  |  |  |  |  |  | } | 
| 249 |  |  |  |  |  |  |  | 
| 250 |  |  |  |  |  |  | # Handles wrapping commands with possible ssh and command prefix | 
| 251 |  |  |  |  |  |  | sub wrap { | 
| 252 | 0 |  |  | 0 |  |  | my $wrap_options = shift; | 
| 253 | 0 |  |  |  |  |  | my $builder      = pop; | 
| 254 | 0 |  |  |  |  |  | my @args         = @_; | 
| 255 | 0 |  |  |  |  |  | my ( $ssh, $username, $hostname, $sudo_username, $pretty ); | 
| 256 |  |  |  |  |  |  |  | 
| 257 | 0 | 0 |  |  |  |  | if ( ref( $args[$#args] ) eq 'IPC::Open3::Callback::Command::CommandOptions' ) { | 
| 258 | 0 |  |  |  |  |  | my $options = pop(@args); | 
| 259 | 0 |  | 0 |  |  |  | $ssh           = $options->get_ssh() || 'ssh'; | 
| 260 | 0 |  |  |  |  |  | $username      = $options->get_username(); | 
| 261 | 0 |  |  |  |  |  | $hostname      = $options->get_hostname(); | 
| 262 | 0 |  |  |  |  |  | $sudo_username = $options->get_sudo_username(); | 
| 263 | 0 |  |  |  |  |  | $pretty        = $options->get_pretty(); | 
| 264 |  |  |  |  |  |  | } | 
| 265 |  |  |  |  |  |  |  | 
| 266 | 0 |  |  |  |  |  | my $destination_command = ''; | 
| 267 | 0 |  | 0 |  |  |  | my $command_separator   = $wrap_options->{command_separator} || ';'; | 
| 268 | 0 |  |  |  |  |  | my $commands            = 0; | 
| 269 | 0 |  |  |  |  |  | foreach my $command ( &$builder(@args) ) { | 
| 270 | 0 | 0 |  |  |  |  | if ( defined($command) ) { | 
| 271 | 0 | 0 |  |  |  |  | if ( $commands++ > 0 ) { | 
| 272 | 0 |  |  |  |  |  | $destination_command .= $command_separator; | 
| 273 | 0 | 0 |  |  |  |  | if ($pretty) { | 
| 274 | 0 |  |  |  |  |  | $destination_command .= "\n"; | 
| 275 |  |  |  |  |  |  | } | 
| 276 |  |  |  |  |  |  | } | 
| 277 |  |  |  |  |  |  |  | 
| 278 | 0 |  |  |  |  |  | $command =~ s/^(.*?[^\\]);$/$1/;    # from find -exec | 
| 279 |  |  |  |  |  |  |  | 
| 280 | 0 |  |  |  |  |  | $command = _sudo_command( $sudo_username, $command ); | 
| 281 |  |  |  |  |  |  |  | 
| 282 | 0 |  |  |  |  |  | $destination_command .= $command; | 
| 283 |  |  |  |  |  |  | } | 
| 284 |  |  |  |  |  |  | } | 
| 285 |  |  |  |  |  |  |  | 
| 286 | 0 | 0 |  |  |  |  | if ( $wrap_options->{subshell} ) { | 
| 287 | 0 |  |  |  |  |  | $destination_command = $wrap_options->{subshell} . _quote_command($destination_command); | 
| 288 |  |  |  |  |  |  | } | 
| 289 |  |  |  |  |  |  |  | 
| 290 | 0 | 0 | 0 |  |  |  | if ( !defined($username) && !defined($hostname) ) { | 
| 291 |  |  |  |  |  |  |  | 
| 292 |  |  |  |  |  |  | # silly to ssh to localhost as current user, so dont | 
| 293 | 0 |  |  |  |  |  | return $destination_command; | 
| 294 |  |  |  |  |  |  | } | 
| 295 |  |  |  |  |  |  |  | 
| 296 | 0 | 0 |  |  |  |  | my $userAt = | 
|  |  | 0 |  |  |  |  |  | 
| 297 |  |  |  |  |  |  | $username | 
| 298 |  |  |  |  |  |  | ? ( ( $ssh =~ /plink(?:\.exe)?$/ ) ? "-l $username " : "$username\@" ) | 
| 299 |  |  |  |  |  |  | : ''; | 
| 300 |  |  |  |  |  |  |  | 
| 301 | 0 |  |  |  |  |  | $destination_command = _quote_command($destination_command); | 
| 302 | 0 |  | 0 |  |  |  | return "$ssh $userAt" . ( $hostname || 'localhost' ) . " $destination_command"; | 
| 303 |  |  |  |  |  |  | } | 
| 304 |  |  |  |  |  |  |  | 
| 305 |  |  |  |  |  |  | package IPC::Open3::Callback::Command::CommandOptions; | 
| 306 |  |  |  |  |  |  | $IPC::Open3::Callback::Command::CommandOptions::VERSION = '1.16'; | 
| 307 | 1 |  |  | 1 |  | 418 | use parent qw(Class::Accessor); | 
|  | 1 |  |  |  |  | 280 |  | 
|  | 1 |  |  |  |  | 4 |  | 
| 308 |  |  |  |  |  |  | __PACKAGE__->follow_best_practice; | 
| 309 |  |  |  |  |  |  | __PACKAGE__->mk_ro_accessors(qw(hostname pretty ssh sudo_username username)); | 
| 310 |  |  |  |  |  |  |  | 
| 311 |  |  |  |  |  |  | sub new { | 
| 312 |  |  |  |  |  |  | my ( $class, @args ) = @_; | 
| 313 |  |  |  |  |  |  | return bless( {}, $class )->_init(@args); | 
| 314 |  |  |  |  |  |  | } | 
| 315 |  |  |  |  |  |  |  | 
| 316 |  |  |  |  |  |  | sub clone { | 
| 317 |  |  |  |  |  |  | my ( $instance, %options ) = @_; | 
| 318 |  |  |  |  |  |  | if ( exists( $instance->{hostname} ) && !exists( $options{hostname} ) ) { | 
| 319 |  |  |  |  |  |  | $options{hostname} = $instance->{hostname}; | 
| 320 |  |  |  |  |  |  | } | 
| 321 |  |  |  |  |  |  | if ( exists( $instance->{ssh} ) && !exists( $options{ssh} ) ) { | 
| 322 |  |  |  |  |  |  | $options{ssh} = $instance->{ssh}; | 
| 323 |  |  |  |  |  |  | } | 
| 324 |  |  |  |  |  |  | if ( exists( $instance->{username} ) && !exists( $options{username} ) ) { | 
| 325 |  |  |  |  |  |  | $options{username} = $instance->{username}; | 
| 326 |  |  |  |  |  |  | } | 
| 327 |  |  |  |  |  |  | if ( exists( $instance->{sudo_username} ) && !exists( $options{sudo_username} ) ) { | 
| 328 |  |  |  |  |  |  | $options{sudo_username} = $instance->{sudo_username}; | 
| 329 |  |  |  |  |  |  | } | 
| 330 |  |  |  |  |  |  | if ( exists( $instance->{pretty} ) && !exists( $options{pretty} ) ) { | 
| 331 |  |  |  |  |  |  | $options{pretty} = $instance->{pretty}; | 
| 332 |  |  |  |  |  |  | } | 
| 333 |  |  |  |  |  |  | return new( ref($instance), %options ); | 
| 334 |  |  |  |  |  |  | } | 
| 335 |  |  |  |  |  |  |  | 
| 336 |  |  |  |  |  |  | sub _init { | 
| 337 |  |  |  |  |  |  | my ( $self, %options ) = @_; | 
| 338 |  |  |  |  |  |  |  | 
| 339 |  |  |  |  |  |  | $self->{hostname}      = $options{hostname}      if ( defined( $options{hostname} ) ); | 
| 340 |  |  |  |  |  |  | $self->{ssh}           = $options{ssh}           if ( defined( $options{ssh} ) ); | 
| 341 |  |  |  |  |  |  | $self->{username}      = $options{username}      if ( defined( $options{username} ) ); | 
| 342 |  |  |  |  |  |  | $self->{sudo_username} = $options{sudo_username} if ( defined( $options{sudo_username} ) ); | 
| 343 |  |  |  |  |  |  | $self->{pretty}        = $options{pretty}        if ( defined( $options{pretty} ) ); | 
| 344 |  |  |  |  |  |  |  | 
| 345 |  |  |  |  |  |  | return $self; | 
| 346 |  |  |  |  |  |  | } | 
| 347 |  |  |  |  |  |  |  | 
| 348 |  |  |  |  |  |  | 1; | 
| 349 |  |  |  |  |  |  |  | 
| 350 |  |  |  |  |  |  | __END__ |