Link to home
Start Free TrialLog in
Avatar of aleyva
aleyva

asked on

Copying binary files

I am writing a Perl program that runs on NT. I have NFS mounted unix filesystems. I will need to copy binary files from the NT system to the Unux filesystem. I have written a subroutines that accepts two parameters, the full source path including filename, and the destination path. I suspect that I would be opening a filehandle to the destination file and opening another to the source. I guess that I would then simply print one to the other, but I think I need binary mode somewhere.

I hope this is clear enough.
Thanx
Angel
ASKER CERTIFIED SOLUTION
Avatar of b2pi
b2pi
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of aleyva
aleyva

ASKER

b2pi, Why don't you have any real profile data? Anyway,

I have a couple of questions/comments.
First of all, what is the carp () command doing?

Next, I have done the following and it works, so I would simply want to ask why it is or isn't a good idea to do it this way.

      open (SRC,"$source");
      open (DEST,">$destination");
      binmode (SRC);
      binmode (DEST);
      print DEST <SRC>;
      close (SRC);
      close (DEST);

Again, this works because I am able to read the files on the UNIX end.

I tried the system ("copy /b $source $destination"); command, but the files never appeared, this is why I am now trying with perl. Not only that, it makes it more portable.

One more, would it be asking too much to have you comment the code? (You must be rolling your eyes by now with that one).

Appreciated
Angel

PS    I increased the points.
1.) Why do I need profile data?
2.) carp issues warnings with a bit of trace info.  I note that I
forgot that at the top of the code there has to be a


use Carp;

line.  Take a look at

perldoc Carp

for more information. (Note that, as with warn, you can defined a
$SIG__WARN__} handler to suppress messages)

3.) There's probably no problem with your code, save for two
bugaboos. First, no error handling, and no way to determine if the
copy worked (or, if it didn't, where, and why not).  Secondly, it will
read the entire file into memory as one chunk.  That could be a bit of
a problem if memory resources are .... constrained.  Suppose you have
a 64M machine, and a 128M file.  Even with virtual memory, you're
going to be waiting a long, long time.

4.) Comment?  It's kind of small, but...

use strict;
use Carp;

## Prototype to put at the top of code
sub MyCP ( $$ );


## Intervening code..., including that which calls MyCP
## like
##
##    die "MyCP failure" if !MyCP($src, $dst)
##

sub MyCP ( $$ ) {
    my($src, $dst) = @_;
    my($error) = 0;
    ## Ensure that the source file exists
    if (!-f "$src") {
        carp "MyCP: Invalid src file '$src'";
        $error ++;
    }
    ## Ensure that the destination file exists
    if (!-f "$dst") {
        carp "MyCP: Invalid dst file '$dst'";
        $error ++;
    }
    return undef if $error;
    ## Open the source file
    open(SRC, "<$src") || $error++;
    if ($error) {
        carp "Unable to open src file '$src' : $!";
        return undef;
    }
    ## Open the destination file
    open(DST, ">$dst") || $error++;
    if ($error) {
        carp "Unable to open dst file '$dst' : $!";
        close(SRC);
        return undef;
    }
    ## Set both source and destination files to binmode
    binmode SRC;
    binmode DST;
    ## Define a constant for maximum read size
    my($count) = 64*1024;
    ## declare some variables (byte count, read count, write count,
    ## a buffer for reads and writes, and bytes remaining)
    my($bcount, $rcount, $wcount, $buffer, $rcount);
    ## Note that -s $src returns the size of the file $src
    for ($bcount = -s $src; $bcount; $bcount -= $wcount) {
      ## Read the data, $rcount is the number of bytes read
        $rcount = sysread(SRC, $buffer, $count);
        $wcount = undef;
      ## Complain on error
        if (!defined($rcount)) {
            carp "src read failed: $!";
        } else {
          ## Write the data, $wcount is the number of bytes written
            $wcount = syswrite(DST, $buffer, $rcount);
          ## Complain on error
            if (!defined($wcount) || $wcount != $rcount) {
                carp "dst write failed: $!";
            }
        }
      ## Complain and return on error
        if (!defined($wcount) || $wcount != $rcount) {
            close(SRC);
            close(DST);
            return undef;
        }
    }
    ## clean up
    close(SRC);
    close(DST);
    # Done
    return 1;
}

Avatar of ozo
b2pi, if you're not going to handle partial writes from the sysewrite, it may be safer to use print, which handles all that for you.
Avatar of aleyva

ASKER

b2pi,

The reason for the profile, is primarily to know your background/experience, and a name, but no big deal.

I implemented your solution, and it works like a champ almost. I don't think that it is your code as much as it might be the file, but I have a postscript file that was saved on a MAC as BINARY Postscript. The programs loops forever trying to write the file even though it is returning 0 values for rcount and wcount. I simply put an check if these values are 0, then exit.

Thanx again,
Angel
ozo, I thought I was handling partial results from the write ($wcount
!= $rcount). It is true that I could retry the write, but, really,
I don't think I've ever had a need.

I find it curious that a sysread could return 0 bytes on a proper call...
You may not have ever had a signal sent to you during a syswrite
(write is different, don't try to mix write and syswrite calls on the same filehandle)
But that doesn't mean you will never have the need.