#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <stddef.h>
#include <string.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <dirent.h>


#define buf_size 4096

void copyfile(const char *path1, const char *path2) {
	int f1, f2;
	// Test et ouverture du fichier source
	if (access(path1, R_OK) != 0) {
		fprintf(stderr, "erreur: vous n'avez pas le droit de lire le fichier source\n");
		exit(EXIT_FAILURE);
	}
	f1 = open(path1, O_RDONLY);
	if (f1 == -1) {
		const char *msg = strerror(errno);
		fprintf(stderr, "erreur: impossible d'ouvrir le fichier source\n\t%s\n", msg);
		exit(EXIT_FAILURE);
	}
	// Test et ouverture du fichier cible
	if (access(path2, F_OK) == 0) {
		fprintf(stderr, "erreur: le fichier cible existe déjà\n");
		exit(EXIT_FAILURE);
	}
	f2 = creat(path2, 0);
	if (f2 == -1) {
		const char *msg = strerror(errno);
		fprintf(stderr, "erreur: impossible de créer le fichier cible\n\t%s\n", msg);
		exit(EXIT_FAILURE);
	}
	// Copie du contenu du fichier
	while (1) {
		char buf[buf_size];
		ssize_t cnt_read;
		ssize_t cnt_write;
		// Lecture d'un bloc dans le fichier source
		cnt_read = read(f1, buf, buf_size);
		if (cnt_read == -1) {
			const char *msg = strerror(errno);
			fprintf(stderr, "erreur: impossible de lire le fichier source\n\t%s\n", msg);
			exit(EXIT_FAILURE);
		}
		// Ecriture du bloc dans le fichier cible
		cnt_write = write(f2, buf, cnt_read);
		if (cnt_write == -1) {
			const char *msg = strerror(errno);
			fprintf(stderr, "erreur: impossible d'écrire le fichier cible\n\t%s\n", msg);
			exit(EXIT_FAILURE);
		}
		// On vérifie que tout à bien été écris
		if (cnt_read != cnt_write) {
			fprintf(stderr, "erreur: pas suffisament d'espace disponible\n");
			exit(EXIT_FAILURE);
		}
		// On test la fin de fichier
		if (cnt_read != buf_size)
			break;
	}
	// Et on ferme les fichiers
	close(f1);
	close(f2);
}

void copyperm(const char *path1, const char *path2) {
	struct stat st;
	// On commence par lire les informations du fichier source
	if (stat(path1, &st) == -1) {
		const char *msg = strerror(errno);
		fprintf(stderr, "erreur: impossible de lire les permissions\n\t%s\n", msg);
		exit(EXIT_FAILURE);
	}
	// Puis on modifie les permission du fichiers cible
	if (chmod(path2, st.st_mode & 0x0FFF)  == -1) {
		const char *msg = strerror(errno);
		fprintf(stderr, "erreur: impossible de changer les permissions\n\t%s\n", msg);
		exit(EXIT_FAILURE);
	}
	// Et on modifie le proprietaire et le groupe
	if (chown(path2, st.st_uid, st.st_gid) == -1) {
		const char *msg = strerror(errno);
		fprintf(stderr, "erreur: impossible de changer le propriétaire\n\t%s\n", msg);
		exit(EXIT_FAILURE);
	}
}

void copydir(const char *path1, const char *path2) {
	int len_path1 = strlen(path1);
	int len_path2 = strlen(path2);
	struct stat st;
	DIR *dir;
	// On vérifie que la source est bien un répertoire
	if (stat(path1, &st) == -1) {
		const char *msg = strerror(errno);
		fprintf(stderr, "erreur: impossible de lire les permissions\n\t%s\n", msg);
		exit(EXIT_FAILURE);
	}
	if ((st.st_mode & S_IFDIR) == 0) {
		fprintf(stderr, "erreur: la source n'est pas un répertoire\n");
		exit(EXIT_FAILURE);
	}
	if (access(path1, R_OK) != 0) {
		fprintf(stderr, "erreur: vous n'avez pas le droit de lire le répertoire source\n");
		exit(EXIT_FAILURE);
	}
	// On crée le répertoire cible
	if (mkdir(path2, 0777) == - 1) {
		const char *msg = strerror(errno);
		fprintf(stderr, "erreur: impossible de lire les permissions\n\t%s\n", msg);
		exit(EXIT_FAILURE);
	}
	// Et on parcours la liste des fichiers
	dir = opendir(path1);
	if (dir == NULL) {
		const char *msg = strerror(errno);
		fprintf(stderr, "erreur: impossible d'ouvrir le répertoire source\n\t%s\n", msg);
		exit(EXIT_FAILURE);
	}
	while (1) {
		char src[len_path1 + _POSIX_NAME_MAX + 1];
		char dst[len_path2 + _POSIX_NAME_MAX + 1];
		// On lit l'élément suivant du répertoire et on test la fin de liste
		struct dirent *item = readdir(dir);
		if (item == NULL)
			break;
		// On test les fichiers spéciaux à ne pas copier
		if (strcmp(item->d_name, ".") == 0)
			continue;
		if (strcmp(item->d_name, "..") == 0)
			continue;
		// On construit les deux noms de fichiers
		strcpy(src, path1);
		strcat(src, item->d_name);
		strcpy(dst, path2);
		strcat(dst, item->d_name);
		// On copie le fichier et ses permissions
		fprintf(stdout, "Copying %s to %s\n", src, dst);
		copyfile(src, dst);
		copyperm(src, dst);
	}
	closedir(dir);
}

int main(int argc, char *argv[]) {
	if (argc != 3) {
		fprintf(stderr, "copy repertoire1 repertoire2\n");
		exit(EXIT_FAILURE);
	}
 	copydir(argv[1], argv[2]);
	//copyfile(argv[1], argv[2]);
	//copyperm(argv[1], argv[2]);

	return EXIT_SUCCESS;
}


