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.
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.)