0

I am trying to make a copy program that allows me to copy file from current directory to another directory. I am using open(), creat(), read(), write() so far.

For example: So far i can only copy the file to my current directory.

I have a folder name A_folder and a file name file_1 and i want to copy file_1 from my current directory to A_folder.

Can someone please help me?

Code and image are shown below

What I can do now: ./copy file1 copy_file

What I want is: ./copy file1 ./folder copy_file

mmm
  • 7
  • 7

1 Answers1

1
  1. You need a helper function that constructs the path you need, A_folder/file_1, when given A_folder and file_1.

    A typical example of such a function is

    char *combine_path(const char *dir, const char *name)
    {
        /* Calculate the lengths of the path components.
           If the respective parameter is NULL, the length is zero .*/
        const size_t  dirlen = (dir) ? strlen(dir) : 0;
        const size_t  namelen = (name) ? strlen(name) : 0;
    
        char         *path, *p;
    
        /* We allocate <dir> + '/' + <name> + '\0'. */
        path = malloc(dirlen + namelen + 2);
        if (!path) {
            errno = ENOMEM;
            return NULL;
        }
    
        /* Let p point to the current position in the
           resulting path. */
        p = path;
    
        /* If there is a directory part,
           copy it, and append a '/' after it. */
        if (dirlen > 0) {
            memcpy(p, dir, dirlen);
            p += dirlen;
            *p = '/';
            p += 1;
        }
    
        /* If there is a name part, copy it. */
        if (namelen > 0) {
            memcpy(p, name, namelen);
            p += namelen;
        }
    
        /* Append a NUL char, '\0', to terminate the
           dynamically allocated buffer.
           This turns it into a C string. */
        *p = '\0';
    
        /* Return the pointer to the dynamically-allocated
           memory, containing the concatenated paths
           as a single string. */
        return path;
    }
    

    Note that the above function returns a dynamically allocated copy, so you should free() the result when you no longer need it.
     

  2. I prefer much more explicit error checking. Consider:

    int copy_file(const char *frompath, const char *topath)
    {
        struct stat frominfo, toinfo;
        char        data[BUFFERSIZE];
        ssize_t     n;
        int         src, dst, cause;
    
        if (!frompath || !*frompath ||
            !*topath || !*topath) {
            fprintf(stderr, "copy_file(): NULL or empty file name!\n");
            return errno = EINVAL;
        }
    
        src = open(frompath, O_RDONLY | O_NOCTTY);
        if (src == -1) {
            cause = errno;
            fprintf(stderr, "%s: Cannot open file: %s.\n", frompath, strerror(cause));
            return errno = cause;
        }
        if (fstat(src, &frominfo) == -1) {
            cause = errno;
            fprintf(stderr, "%s: Cannot get file statistics: %s.\n", frompath, strerror(cause));
            return errno = cause;
        }
    
        dst = open(topath, O_WRONLY | O_CREAT | O_EXCL, frominfo.st_mode & 0777);
        if (dst == -1) {
            cause = errno;
            fprintf(stderr, "%s: Cannot create file: %s.\n", topath, strerror(saved_errno));
            errno = cause;
        }
    
        while (1) {
            char *p, *q;
    
            n = read(src, buffer, sizeof buffer);
            if (n == 0) {
                /* End of input. */
                cause = 0;
                break;
            } else
            if (n < 0) {
                /* Error (or interrupt, EINTR). */
                if (n == -1)
                    cause = errno;
                else
                    cause = EIO; /* n < -1 is a bug. */
                fprintf(stderr, "%s: Read error: %s.\ņ", frompath, strerror(cause));
                break;
            }
    
            p = buffer;
            q = n;
            cause = 0;
            while (p < q) {
                n = write(dst, p, (size_t)(q - p));
                if (n > 0)
                    p += n;
                else
                if (n == -1) {
                    cause = errno;
                    break;
                else {
                    /* Bug; should never occur. */
                    cause = EIO;
                    break;
                }
            }
            if (cause) {
                fprintf(stderr, "%s: Write error: %s.\n", topath, strerror(cause));
                break;
            }
        }
    
        /* Failed? */
        if (cause) {
            unlink(topath);
            return errno = cause;
        }
    
        if (fstat(dst, &toinfo) == -1) {
            cause = errno;
            fprintf(stderr, "%s: Cannot get file information: %s.\n", topath, strerror(cause));
            unlink(topath);
            return errno = cause;
        }
    
        /* from may be a device; if so, its size
           will be zero. */
        if (frominfo.st_size > 0 &&
            frominfo.st_size != toinfo.st_size) {
            cause = EIO;
            fprintf(stderr, "%s: File size mismatch!\n", topath);
            unlink(topath);
            return errno = cause;
        }
    
        /* Careful closing. */
        if (close(src) == -1) {
            cause = errno;
            fprintf(stderr, "%s: Error closing file: %s.\n", frompath, strerror(cause));
            unlink(topath);
            return errno = cause;
        }
    
        if (close(dst) == -1) {
            cause = errno;
            fprintf(stderr, "%s: Error closing file: %s.\n", topath, strerror(cause));
            unlink(topath);
            return errno = cause;
        }
    
        /* Success. */
        return errno = 0;
    }
    

    Note the pattern how I use p and q pointers to write the read buffer contents in possibly more than one part. This can occur, if the source file is local, and the target file is on some wonky filesystem. There is no requirement that write() should either write the entire buffer or return with an error code; short writes -- where only some of the initial data in the given buffer was written -- are perfectly okay, and do occur in some situations. The above is my preferred way to handle those.

    Many consider this level of error checking -- especially checking the result value of close(), since at this point in time many operating systems (including Linux) never return an error there -- foolish or at least paranoid.

    I personally consider this kind of error checking "robust", because I want my code to tell me, as the user, if anything untoward happened; I definitely do not want it to just assume everything went fine, I want it to be paranoid about it. (I am not saying that OP's code does not check error codes; I'm only saying this version is more careful and vocal about it.)

Nominal Animal
  • 38,216
  • 5
  • 59
  • 86
  • Thanks for your help, I'll give it a shot. – mmm Apr 27 '17 at 15:08
  • Would you mind explain solution 1 in more details? Some of the function are out of my understanding. X ( – mmm Apr 27 '17 at 15:14
  • @mmm: I re-commented the `combine_path()` function. Can you understand it now? You can find the documentation for [`malloc()`](http://man7.org/linux/man-pages/man3/malloc.3.html), [`strlen()`](http://man7.org/linux/man-pages/man3/strlen.3.html), and [`memcpy()`](http://man7.org/linux/man-pages/man3/memcpy.3.html) at the [man pages](http://man7.org/linux/man-pages/index.html), i.e. running `man 3 malloc`, `man 3 strlen`, or `man 3 memcpy` at the command line. – Nominal Animal Apr 27 '17 at 15:21
  • Thank you very much. One last thing, any recommendation for Linux programming in C? I wish I can be the one solving question instead of asking it. – mmm Apr 27 '17 at 15:25
  • @mmm: Unfortunately, no -- but I love your attitude :). Most of the tutorials and guides on the web are quite poor. There are two things that you can look for in tutorials, though. When reading input line-by-line, a good tutorial should show how to do it using POSIX.1-2008 [`getline()`](http://man7.org/linux/man-pages/man3/getline.3.html). When dealing with network connections, the tutorial should use [`getaddrinfo()`](http://man7.org/linux/man-pages/man3/getaddrinfo.3.html). Any tutorial that deals with those topics but use something else are not worth reading. – Nominal Animal Apr 27 '17 at 19:06