From 8133bc4ed769b7e0795ac5bbc86cd210247e4540 Mon Sep 17 00:00:00 2001
From: "tanguy.cavagna" <tanguy.cavagna@etu.hesge.ch>
Date: Mon, 27 Jun 2022 17:43:25 +0200
Subject: [PATCH] Can copy directory content except links and date attributes

---
 main.c              |   5 +-
 ultra-cp/ultra-cp.c | 129 +++++++++++++++++++++++++++++++++++++-------
 ultra-cp/ultra-cp.h |  12 +++++
 3 files changed, 124 insertions(+), 22 deletions(-)

diff --git a/main.c b/main.c
index ab9e9bc..c182b2c 100644
--- a/main.c
+++ b/main.c
@@ -26,14 +26,13 @@ int main(int argc, char **argv) {
             source_paths[i][strlen(source_paths[i]) - 1] = '\0';
     }
 
-    // TODO: Copy files form sources to destination
+    // Copy content
+    copy_files((const char **)source_paths, destination_path, opt, source_count);
 
     // Free sources
     for (int i = 0; i < source_count; i++)
         free(source_paths[i]);
     free(source_paths);
 
-    list_dir("/home/toguy/Documents/source");
-
     return EXIT_SUCCESS;
 }
diff --git a/ultra-cp/ultra-cp.c b/ultra-cp/ultra-cp.c
index 4e0e8e1..d8a31d8 100644
--- a/ultra-cp/ultra-cp.c
+++ b/ultra-cp/ultra-cp.c
@@ -1,34 +1,118 @@
 #include "ultra-cp.h"
-#include <stdlib.h>
-#include <sys/stat.h>
-#include <time.h>
 
 //====================
 //      PRIVATE
 //====================
+/**
+ * @brief Get the entry permissions as int
+ *
+ * @param path Entry path
+ *
+ * @returns Permissions
+ */
+int get_int_permissions(const char *path) {
+    struct stat entry_stat;
+
+    if (stat(path, &entry_stat) < 0)
+        return -1;
+
+    return entry_stat.st_mode;
+}
+
+/**
+ * @brief Copy a source entry to a destination location
+ *
+ * @param source Source entry
+ * @param destination Destination location
+ */
+void copy_entry(struct dirent *entry, const char *source,
+                const char *destination) {
+    // Entry is a file, copy it
+    if (entry->d_type == DT_REG) {
+        FILE *src_file, *dst_file;
+        src_file = fopen(source, "rb");
+        dst_file = fopen(destination, "wb");
+
+        size_t n;
+        char buff[BUFSIZ];
+        while ((n = fread(buff, 1, BUFSIZ, src_file)) != 0)
+            fwrite(buff, 1, n, dst_file);
+
+        fclose(src_file);
+        fclose(dst_file);
+
+        chmod(destination, get_int_permissions(source));
+    }
+
+    // Go deeper if directory
+    if (entry->d_type == DT_DIR)
+        mkdir(destination, get_int_permissions(source));
+}
+
+/**
+ * @brief Copy the content of a directory to a destination
+ *
+ * @param source Source directory
+ * @param destination Destination directory
+ */
+void copy_directory(const char *source, const char *destination) {
+    DIR *dir;
+    struct dirent *entry;
+
+    // Cannot open dir
+    if (!(dir = opendir(source)))
+        return;
+
+    // Loop through all entries of the given directory
+    while ((entry = readdir(dir)) != NULL) {
+        const char *d_name;
+        d_name = entry->d_name;
+
+        // Skip "." and ".."
+        if (strcmp(d_name, ".") == 0 || strcmp(d_name, "..") == 0)
+            continue;
+
+        int dst_path_lenght;
+        char dst_path[PATH_MAX];
+        char src_path[PATH_MAX];
+
+        // Create the full source and destination paths
+        snprintf(src_path, PATH_MAX, "%s/%s", source, d_name);
+        dst_path_lenght =
+            snprintf(dst_path, PATH_MAX, "%s/%s", destination, d_name);
+
+        // Path length exceeded clause
+        if (dst_path_lenght >= PATH_MAX) {
+            fprintf(stderr, "Path length has got too long.\n");
+            exit(EXIT_FAILURE);
+        }
+
+        copy_entry(entry, src_path, dst_path);
+
+        if (entry->d_type == DT_DIR)
+            copy_directory(src_path, dst_path);
+    }
+
+    closedir(dir);
+}
 
 //====================
 //      PUBLIC
 //====================
 char *get_permissions(const char *name) {
-    struct stat file_stat;
-
-    // Read stats
-    if (stat(name, &file_stat) < 0)
-        return NULL;
-
+    int perms = get_int_permissions(name);
     char *permissions = calloc(10, sizeof(char));
 
-    permissions[0] = S_ISDIR(file_stat.st_mode) ? 'd' : S_ISLNK(file_stat.st_mode) ? 'l' : '-';
-    permissions[1] = (file_stat.st_mode & S_IRUSR) ? 'r' : '-';
-    permissions[2] = (file_stat.st_mode & S_IWUSR) ? 'w' : '-';
-    permissions[3] = (file_stat.st_mode & S_IXUSR) ? 'x' : '-';
-    permissions[4] = (file_stat.st_mode & S_IRGRP) ? 'r' : '-';
-    permissions[5] = (file_stat.st_mode & S_IWGRP) ? 'w' : '-';
-    permissions[6] = (file_stat.st_mode & S_IXGRP) ? 'x' : '-';
-    permissions[7] = (file_stat.st_mode & S_IROTH) ? 'r' : '-';
-    permissions[8] = (file_stat.st_mode & S_IWOTH) ? 'w' : '-';
-    permissions[9] = (file_stat.st_mode & S_IXOTH) ? 'x' : '-';
+    permissions[0] = S_ISDIR(perms) ? 'd' : S_ISLNK(perms) ? 'l' : '-';
+    permissions[1] = (perms & S_IRUSR) ? 'r' : '-';
+    permissions[2] = (perms & S_IWUSR) ? 'w' : '-';
+    permissions[3] = (perms & S_IXUSR) ? 'x' : '-';
+    permissions[4] = (perms & S_IRGRP) ? 'r' : '-';
+    permissions[5] = (perms & S_IWGRP) ? 'w' : '-';
+    permissions[6] = (perms & S_IXGRP) ? 'x' : '-';
+    permissions[7] = (perms & S_IROTH) ? 'r' : '-';
+    permissions[8] = (perms & S_IWOTH) ? 'w' : '-';
+    permissions[9] = (perms & S_IXOTH) ? 'x' : '-';
 
     return permissions;
 }
@@ -104,3 +188,10 @@ void list_dir(const char *name) {
 
     closedir(dir);
 }
+
+void copy_files(const char **sources, const char *destination, uint8_t options,
+                int source_count) {
+    for (int s = 0; s < source_count; s++) {
+        copy_directory(sources[s], destination);
+    }
+}
diff --git a/ultra-cp/ultra-cp.h b/ultra-cp/ultra-cp.h
index b7ffca4..8f3878a 100644
--- a/ultra-cp/ultra-cp.h
+++ b/ultra-cp/ultra-cp.h
@@ -2,6 +2,8 @@
 #define ULTRA_CP_
 
 #include <dirent.h>
+#include <linux/limits.h>
+#include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -44,4 +46,14 @@ char *get_date(const char *name);
  */
 void list_dir(const char *name);
 
+/**
+ * @brief Copy all content from sources to destination
+ *
+ * @param sources List of all sources to copy
+ * @param destination Destination for the content
+ * @param options Copy options
+ * @param source_count Source count
+ */
+void copy_files(const char **sources, const char *destination, uint8_t options,
+                int source_count);
 #endif // !ULTRA_CP_
-- 
GitLab