Home > Blockchain >  Copy files into a directory using call systems
Copy files into a directory using call systems

Time:09-26

I have a program that take two files Source and Destination.
I am trying to Copies the SOURCE file to the DESTINATION file. If DESTINATION does not exist, it is created. If DESTINATION exists and is a file, it is overwritten. If DESTINATION exists and is a directory, the source files will be copied there.

How can i copy the source file into the destination directory using calls system ?

What i tried :

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include "checksum.h"

int main (int argc, char *argv[])
{
    if (argc < 3) {
        fprintf(stderr, "Usage: lcp [-b taille] source... destination\n");
        exit(1);
    }

    int fd1 = open(argv[1], O_RDONLY, 0644); //0644 — owner can read or write, group and others can only read; or something more restrictive).
    if (fd1 < 0)
    {
        fprintf(stderr, "%s: failed to open file %s for reading\n", argv[0], argv[1]);
        return 1;
    }
    int fd2 = open(argv[2], O_RDWR|O_CREAT);  
    if (fd2 < 0)
    {
        fprintf(stderr, "%s: failed to open file %s for reading and writing\n", argv[0], argv[2]);
        return 1;
    }

    close(fd1);  
    close(fd2);  
    return 0;  
}

CodePudding user response:

You just need a loop that does a read on a buffer and a write of that buffer.

Note that most of the time if you ask read to get (e.g.) 100 bytes, it will do so for most files. It may return less than that, particularly if the file size is not a multiple of 100. So, we must account for "short" reads (and errors). Likewise for write

Here is the refactored code. It is annotated:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
//#include "checksum.h"

// xread -- read in data (account for short reads and errors)
// RETURNS: number of bytes read
ssize_t
xread(int fd,void *buf,size_t buflen)
{
    unsigned char *bp = buf;
    ssize_t curlen;
    ssize_t totlen = 0;

    // continue reading until end of buffer or read error
    for (;  buflen > 0;  buflen -= curlen, totlen  = curlen) {
        // read next chunk
        // NOTE: curlen may be less than buflen
        curlen = read(fd,&bp[totlen],buflen);

        // got an EOF
        if (curlen == 0)
            break;

        // read error
        if (curlen < 0) {
            // handle interruptions from signals
            if (errno == EINTR)
                continue;
            perror("xread");
            exit(1);
        }
    }

    return totlen;
}

// xwrite -- write out data (account for short reads and errors)
// RETURNS: number of bytes written
ssize_t
xwrite(int fd,const void *buf,size_t buflen)
{
    const unsigned char *bp = buf;
    ssize_t curlen;
    ssize_t totlen = 0;

    // continue writing until end of buffer or write error
    for (;  buflen > 0;  buflen -= curlen, totlen  = curlen) {
        // write next chunk
        // NOTE: curlen may be less than buflen
        curlen = write(fd,&bp[totlen],buflen);

        // got EOF (NOTE: this should _never_ happen)
        if (curlen == 0)
            break;

        // write error
        if (curlen < 0) {
            // handle interruptions from signals
            if (errno == EINTR)
                continue;
            perror("xwrite");
            exit(1);
        }
    }

    return totlen;
}

int
main(int argc, char *argv[])
{
    if (argc < 3) {
        fprintf(stderr, "Usage: lcp [-b taille] source... destination\n");
        exit(1);
    }

    // 0644 — owner can read or write, group and others can only read; or
    // something more restrictive).
    int fd1 = open(argv[1], O_RDONLY, 0644);
    if (fd1 < 0) {
        fprintf(stderr, "%s: failed to open file %s for reading\n",
            argv[0], argv[1]);
        return 1;
    }

    int fd2 = open(argv[2], O_RDWR | O_CREAT, 0644);
    if (fd2 < 0) {
        fprintf(stderr, "%s: failed to open file %s for reading and writing\n",
            argv[0], argv[2]);
        return 1;
    }

    // loop through file and copy bytes
    unsigned char buf[64 * 1024];
    while (1) {
        ssize_t rlen = xread(fd1,buf,sizeof(buf));
        if (rlen <= 0)
            break;
        xwrite(fd2,buf,rlen);
    }

    close(fd1);
    close(fd2);

    return 0;
}

Note: As Andrew pointed out, the open with O_CREAT needs a mode argument


UPDATE:

If destination is a directory, the code fails spectacularly. And don't just say failed to open. Please strerror the reason. – – user58697

Yes, I only added the read/write loop since that was the main problem.

@user58697 I had the same problem that blocked me, do you have any idea how to fix this... – elfii

Okay, there are a few ways to solve this (which was part of the original problem that I missed):

  1. Use stat on the output file to decide if it's a directory
  2. Check errno from the open and see if it's EISDIR

While both are valid, I chose (2):

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
//#include "checksum.h"

// xread -- read in data (account for short reads and errors)
// RETURNS: number of bytes read
ssize_t
xread(int fd,void *buf,size_t buflen)
{
    unsigned char *bp = buf;
    ssize_t curlen;
    ssize_t totlen = 0;

    // continue reading until end of buffer or read error
    for (;  buflen > 0;  buflen -= curlen, totlen  = curlen) {
        // read next chunk
        // NOTE: curlen may be less than buflen
        curlen = read(fd,&bp[totlen],buflen);

        // got an EOF
        if (curlen == 0)
            break;

        // read error
        if (curlen < 0) {
            // handle interruptions from signals
            if (errno == EINTR)
                continue;
            perror("xread");
            exit(1);
        }
    }

    return totlen;
}

// xwrite -- write out data (account for short reads and errors)
// RETURNS: number of bytes written
ssize_t
xwrite(int fd,const void *buf,size_t buflen)
{
    const unsigned char *bp = buf;
    ssize_t curlen;
    ssize_t totlen = 0;

    // continue writing until end of buffer or write error
    for (;  buflen > 0;  buflen -= curlen, totlen  = curlen) {
        // write next chunk
        // NOTE: curlen may be less than buflen
        curlen = write(fd,&bp[totlen],buflen);

        // got EOF (NOTE: this should _never_ happen)
        if (curlen == 0)
            break;

        // write error
        if (curlen < 0) {
            // handle interruptions from signals
            if (errno == EINTR)
                continue;
            perror("xwrite");
            exit(1);
        }
    }

    return totlen;
}

int
main(int argc, char **argv)
{

    if (argc < 3) {
        fprintf(stderr, "Usage: lcp [-b taille] source... destination\n");
        exit(1);
    }

    // input file
    char *ifile = argv[1];
    char *itail = ifile;
    int fd1;

    // output file
    char *otail = argv[2];
    char ofile[strlen(otail)   1   strlen(ifile)   1];
    int fd2;

    // 0644 — owner can read or write, group and others can only read; or
    // something more restrictive).
    fd1 = open(ifile, O_RDONLY);
    if (fd1 < 0) {
        fprintf(stderr, "%s: failed to open file %s for reading -- %s\n",
            argv[0], itail, strerror(errno));
        return 1;
    }

    // try original output file and then [if directory] the concatenated file
    for (int tryno = 1;  tryno <= 2;    tryno) {
        // open output assuming arg is flat file
        fd2 = open(otail, O_RDWR | O_CREAT | O_TRUNC, 0644);
        if (fd2 >= 0)
            break;

        // if output is not a directory, there is another error
        if (errno != EISDIR)
            break;

        // get file tail from input file
        // (e.g. foo/bar --> bar
        itail = strrchr(ifile,'/');
        if (itail != NULL)
              itail;
        else
            itail = ifile;

        // create output file: otail / itail
        strcpy(ofile,otail);
        strcat(ofile,"/");
        strcat(ofile,itail);

        otail = ofile;
    }

    // stop if output could not be opened
    if (fd2 < 0) {
        fprintf(stderr, "%s: failed to open file %s for writing -- %s\n",
            argv[0], otail, strerror(errno));
        return 1;
    }

    // loop through files and copy bytes
    unsigned char buf[64 * 1024];
    while (1) {
        ssize_t rlen = xread(fd1,buf,sizeof(buf));
        if (rlen <= 0)
            break;
        xwrite(fd2,buf,rlen);
    }

    close(fd1);
    close(fd2);

    return 0;
}

CodePudding user response:

If DESTINATION exists and is a file, it is overwritten. If DESTINATION exists and is a directory, the source files will be copied there.

The easiest way to address this is to check how open failed`:

    int fd2 = open(argv[2], O_RDWR|O_CREAT);  
    if (fd2 < 0) {
        if (errno != EISDIR) {
            // It is a real failure. Log it, and quit
            fprintf("Opening %s: %s\n", argv[2], strerror(errno));
            return 1;
        }
        // argv[2] names a directory. Act acordingly.

Acting accordingly depends on the precise spec. Consider a scenario:

> mkdir foo
> copy bar/baz/blah foo

As a result, one may expect a destination to be foo/blah. Another may expect it to be foo/bar/baz/blah.

In any case, you shall construct - according to the spec - the true destination filename from argv[1] relative to argv[2], and open it for writing. Few calls to mkdir may also be required.

  • Related