#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <sys/stat.h>
#include <errno.h>

int main(int argc, char *argv[]) {
	struct stat statres;
	int fd1, fd2;
	ssize_t nbytes;
	char buf[BUFSIZ];
	int whicherr;
	char *r, dest_fname[PATH_MAX];

	if (argc != 3) {
		fprintf(stderr, "usage is: %s source destination\n", argv[0]);
		exit(1);
	}

	
	/* Check if 2nd arg is directory; if so append filename portion of
	argv[1] */
	if ((stat(argv[2], &statres) == 0) && (S_ISDIR(statres.st_mode))) {
		r = strrchr(argv[1], '/');
		if (r == NULL)
			r = argv[1];
		else
			r++;
		/* use snprintf to ensure that bytes stored in dest_fname
		are not more than its size */
		snprintf(dest_fname, PATH_MAX, "%s/%s", argv[2], r);
	}
	else {
		strncpy(dest_fname, argv[2], PATH_MAX);
	}
	/* ready to do rename */
	if (rename(argv[1], dest_fname) == -1) {
		/* check if error == EXDEV */
		if (errno == EXDEV) {
			/* first, verify that the first object is not a directory */
			if ((lstat(argv[1], &statres) == 0) && (S_ISDIR(statres.st_mode))) {
				fprintf(stderr, "%s: %s: cannot move a directory accross devices\n", argv[0], argv[1]);
				exit(1);
			}
			if ((fd1 = open(argv[1], O_RDONLY)) == -1) {
				perror(argv[1]);
				exit(1);
			}
			if ((fd2 = open(dest_fname, O_WRONLY | O_CREAT | O_TRUNC, 0666)) == -1) {
				close(fd1);
				perror(dest_fname);
				exit(1);
			}
			while ((nbytes = read(fd1, buf, BUFSIZ)) > 0) {
				if (write(fd2, buf, (size_t)nbytes) != (size_t)nbytes) {
					perror("write failed");
					close(fd1);
					close(fd2);
					unlink(dest_fname);
					exit(1);
				}
			}
			/* save errno; if problems occur with "close" calls
			they will overwrite existing value */
			whicherr = errno;
			close(fd1);
			close(fd2);
			if (nbytes < 0) {
				/* something bad happened */
				fprintf(stderr, "could not read from %s: %s", argv[1], strerror(whicherr));
				unlink(dest_fname);
			}
			else {
				/* all ok */
				unlink(argv[1]);
			}
		}
		else {
			/* In all other cases, print error message and die */
			perror("cannot move");
		}
	}
}

