]> git.seodisparate.com - SimpleArchiver/commitdiff
Impl. "-C <dir>", refactorings
authorStephen Seo <seo.disparate@gmail.com>
Fri, 26 Jul 2024 03:39:56 +0000 (12:39 +0900)
committerStephen Seo <seo.disparate@gmail.com>
Fri, 26 Jul 2024 03:39:56 +0000 (12:39 +0900)
src/archiver.c
src/archiver.h
src/helpers.c
src/helpers.h
src/parser.c
src/parser.h
src/parser_internal.h
src/test.c

index 07d38f47be6604074cd680b9c71463f34240e789..6a3a1c764dc7a46f28b1545d12d1d9aae8773f78 100644 (file)
@@ -875,7 +875,9 @@ void cleanup_free_fn(void *data) { free(data); }
 
 int filenames_to_abs_map_fn(void *data, void *ud) {
   SDArchiverFileInfo *file_info = data;
-  SDArchiverHashMap **abs_filenames = ud;
+  void **ptr_array = ud;
+  SDArchiverHashMap **abs_filenames = ptr_array[0];
+  const char *user_cwd = ptr_array[1];
 
   // Get combined full path to file.
   char *fullpath = filename_to_absolute_path(file_info->filename);
@@ -892,11 +894,16 @@ int filenames_to_abs_map_fn(void *data, void *ud) {
 #if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN || \
     SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC ||          \
     SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX
-  cwd_dirname = realpath(".", NULL);
+  if (user_cwd) {
+    cwd_dirname = realpath(user_cwd, NULL);
+  } else {
+    cwd_dirname = realpath(".", NULL);
+  }
 #endif
   if (!cwd_dirname) {
     return 1;
   }
+  // fprintf(stderr, "cwd_dirname: %s\n", (char*)cwd_dirname);
 
   // Use copy of fullpath to avoid clobbering it.
   __attribute__((cleanup(free_malloced_memory))) void *fullpath_copy =
@@ -947,6 +954,8 @@ char *simple_archiver_error_to_string(enum SDArchiverStateReturns error) {
       return "Failed to create set of filenames (internal error)";
     case SDAS_FAILED_TO_EXTRACT_SYMLINK:
       return "Failed to extract symlink (internal error)";
+    case SDAS_FAILED_TO_CHANGE_CWD:
+      return "Failed to change current working directory";
     default:
       return "Unknown error";
   }
@@ -978,10 +987,14 @@ int simple_archiver_write_all(FILE *out_f, SDArchiverState *state,
   // First create a "set" of absolute paths to given filenames.
   __attribute__((cleanup(simple_archiver_hash_map_free)))
   SDArchiverHashMap *abs_filenames = simple_archiver_hash_map_init();
-  if (simple_archiver_list_get(filenames, filenames_to_abs_map_fn,
-                               &abs_filenames)) {
+  void **ptr_array = malloc(sizeof(void *) * 2);
+  ptr_array[0] = &abs_filenames;
+  ptr_array[1] = (void *)state->parsed->user_cwd;
+  if (simple_archiver_list_get(filenames, filenames_to_abs_map_fn, ptr_array)) {
+    free(ptr_array);
     return SDAS_FAILED_TO_CREATE_MAP;
   }
+  free(ptr_array);
 
   if (fwrite("SIMPLE_ARCHIVE_VER", 1, 18, out_f) != 18) {
     return SDAS_FAILED_TO_WRITE;
@@ -1102,6 +1115,16 @@ int simple_archiver_parse_archive_info(FILE *in_f, int do_extract,
     return SDAS_INVALID_FILE;
   }
 
+  if (do_extract && state->parsed->user_cwd) {
+#if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN || \
+    SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC ||          \
+    SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX
+    if (chdir(state->parsed->user_cwd)) {
+      return SDAS_FAILED_TO_CHANGE_CWD;
+    }
+#endif
+  }
+
   __attribute__((cleanup(free_malloced_memory))) void *decompressor_cmd = NULL;
 
   if ((buf[0] & 1) != 0) {
index dfa1289a309238007750080894d63e1f25cef39d..8d2de1a59ff3d1a0646eca419e36cf8b577023a1 100644 (file)
@@ -46,7 +46,8 @@ enum SDArchiverStateReturns {
   SDAS_INVALID_FILE,
   SDAS_INTERNAL_ERROR,
   SDAS_FAILED_TO_CREATE_MAP,
-  SDAS_FAILED_TO_EXTRACT_SYMLINK
+  SDAS_FAILED_TO_EXTRACT_SYMLINK,
+  SDAS_FAILED_TO_CHANGE_CWD
 };
 
 /// Returned pointer must not be freed.
index 385d511413921f23044b16b732c41135a8d92031..3e2df43048e1a3dbd3550c89902592717cef6c64 100644 (file)
@@ -191,3 +191,28 @@ int simple_archiver_helper_make_dirs(const char *file_path) {
   return 1;
 #endif
 }
+
+char *simple_archiver_helper_cut_substr(const char *s, unsigned int start_idx,
+                                        unsigned int end_idx) {
+  unsigned int s_len = strlen(s);
+  if (start_idx > end_idx || start_idx >= s_len || end_idx > s_len) {
+    return NULL;
+  } else if (end_idx == s_len) {
+    if (start_idx == 0) {
+      return NULL;
+    }
+    char *new_s = malloc(start_idx + 1);
+    strncpy(new_s, s, start_idx + 1);
+    new_s[start_idx] = 0;
+    return new_s;
+  } else if (start_idx == 0) {
+    char *new_s = malloc(s_len - end_idx + 1);
+    strncpy(new_s, s + end_idx, s_len - end_idx + 1);
+    return new_s;
+  } else {
+    char *new_s = malloc(start_idx + s_len - end_idx + 1);
+    strncpy(new_s, s, start_idx);
+    strncpy(new_s + start_idx, s + end_idx, s_len - end_idx + 1);
+    return new_s;
+  }
+}
index 809076cf1ebcd9304a678ff5ebf779a8c7720e85..643d54f5d850dd69a3e050f1f754d2c796e07a72 100644 (file)
@@ -46,4 +46,10 @@ void simple_archiver_helper_cmd_string_argv_free_ptr(char ***argv_strs);
 /// Returns zero on success.
 int simple_archiver_helper_make_dirs(const char *file_path);
 
+/// Returns non-NULL on success.
+/// Must be free'd with "free()" if non-NULL.
+/// start_idx is inclusive and end_idx is exclusive.
+char *simple_archiver_helper_cut_substr(const char *s, unsigned int start_idx,
+                                        unsigned int end_idx);
+
 #endif
index b18b5e7350835c27341d61ce5c14823d5667d079..be1a1e395f157c135b39dd76107a557c9b8b896c 100644 (file)
@@ -39,7 +39,7 @@
 #include "parser_internal.h"
 
 /// Gets the first non "./"-like character in the filename.
-unsigned int simple_archiver_parser_internal_filename_idx(
+unsigned int simple_archiver_parser_internal_get_first_non_current_idx(
     const char *filename) {
   unsigned int idx = 0;
   unsigned int known_good_idx = 0;
@@ -142,6 +142,10 @@ void simple_archiver_print_usage(void) {
   fprintf(stderr,
           "  Use \"-f -\" to work on stdout when creating archive or stdin "
           "when reading archive\n");
+  fprintf(stderr, "  NOTICE: \"-f\" is not affected by \"-C\"!\n");
+  fprintf(stderr,
+          "-C <dir> : Change current working directory before "
+          "archiving/extracting\n");
   fprintf(stderr,
           "--compressor <full_compress_cmd> : requires --decompressor\n");
   fprintf(stderr,
@@ -175,6 +179,7 @@ SDArchiverParsed simple_archiver_create_parsed(void) {
   parsed.decompressor = NULL;
   parsed.working_files = NULL;
   parsed.temp_dir = NULL;
+  parsed.user_cwd = NULL;
 
   return parsed;
 }
@@ -239,6 +244,15 @@ int simple_archiver_parse_args(int argc, const char **argv,
         }
         --argc;
         ++argv;
+      } else if (strcmp(argv[0], "-C") == 0) {
+        if (argc < 2) {
+          fprintf(stderr, "ERROR: -C specified but missing argument!\n");
+          simple_archiver_print_usage();
+          return 1;
+        }
+        out->user_cwd = argv[1];
+        --argc;
+        ++argv;
       } else if (strcmp(argv[0], "--compressor") == 0) {
         if (argc < 2) {
           fprintf(stderr, "--compressor specfied but missing argument!\n");
@@ -286,11 +300,31 @@ int simple_archiver_parse_args(int argc, const char **argv,
       if (out->working_files == NULL) {
         out->working_files = malloc(sizeof(char *) * 2);
         unsigned int arg_idx =
-            simple_archiver_parser_internal_filename_idx(argv[0]);
-        int arg_length = strlen(argv[0] + arg_idx) + 1;
+            simple_archiver_parser_internal_get_first_non_current_idx(argv[0]);
+        unsigned int arg_length = strlen(argv[0] + arg_idx) + 1;
         out->working_files[0] = malloc(arg_length);
         strncpy(out->working_files[0], argv[0] + arg_idx, arg_length);
         simple_archiver_parser_internal_remove_end_slash(out->working_files[0]);
+        if (out->user_cwd) {
+          if (out->user_cwd[strlen(out->user_cwd) - 1] != '/') {
+            char *temp = malloc(strlen(out->user_cwd) + 1 +
+                                strlen(out->working_files[0]) + 1);
+            strncpy(temp, out->user_cwd, strlen(out->user_cwd) + 1);
+            temp[strlen(out->user_cwd)] = '/';
+            strncpy(temp + strlen(out->user_cwd) + 1, out->working_files[0],
+                    strlen(out->working_files[0]) + 1);
+            free(out->working_files[0]);
+            out->working_files[0] = temp;
+          } else {
+            char *temp = malloc(strlen(out->user_cwd) +
+                                strlen(out->working_files[0]) + 1);
+            strncpy(temp, out->user_cwd, strlen(out->user_cwd) + 1);
+            strncpy(temp + strlen(out->user_cwd), out->working_files[0],
+                    strlen(out->working_files[0]) + 1);
+            free(out->working_files[0]);
+            out->working_files[0] = temp;
+          }
+        }
         out->working_files[1] = NULL;
       } else {
         int working_size = 1;
@@ -307,13 +341,37 @@ int simple_archiver_parse_args(int argc, const char **argv,
         // Set new actual last element to NULL.
         out->working_files[working_size] = NULL;
         unsigned int arg_idx =
-            simple_archiver_parser_internal_filename_idx(argv[0]);
+            simple_archiver_parser_internal_get_first_non_current_idx(argv[0]);
         int size = strlen(argv[0] + arg_idx) + 1;
         // Set last element to the arg.
         out->working_files[working_size - 1] = malloc(size);
         strncpy(out->working_files[working_size - 1], argv[0] + arg_idx, size);
         simple_archiver_parser_internal_remove_end_slash(
             out->working_files[working_size - 1]);
+        if (out->user_cwd) {
+          if (out->user_cwd[strlen(out->user_cwd) - 1] != '/') {
+            char *temp =
+                malloc(strlen(out->user_cwd) + 1 +
+                       strlen(out->working_files[working_size - 1]) + 1);
+            strncpy(temp, out->user_cwd, strlen(out->user_cwd) + 1);
+            temp[strlen(out->user_cwd)] = '/';
+            strncpy(temp + strlen(out->user_cwd) + 1,
+                    out->working_files[working_size - 1],
+                    strlen(out->working_files[working_size - 1]) + 1);
+            free(out->working_files[working_size - 1]);
+            out->working_files[working_size - 1] = temp;
+          } else {
+            char *temp =
+                malloc(strlen(out->user_cwd) +
+                       strlen(out->working_files[working_size - 1]) + 1);
+            strncpy(temp, out->user_cwd, strlen(out->user_cwd) + 1);
+            strncpy(temp + strlen(out->user_cwd),
+                    out->working_files[working_size - 1],
+                    strlen(out->working_files[working_size - 1]) + 1);
+            free(out->working_files[working_size - 1]);
+            out->working_files[working_size - 1] = temp;
+          }
+        }
       }
     }
 
@@ -366,12 +424,13 @@ SDArchiverLinkedList *simple_archiver_parsed_to_filenames(
   for (char **iter = parsed->working_files; iter && *iter; ++iter) {
     struct stat st;
     memset(&st, 0, sizeof(struct stat));
-    fstatat(AT_FDCWD, *iter, &st, AT_SYMLINK_NOFOLLOW);
+    char *file_path = *iter;
+    fstatat(AT_FDCWD, file_path, &st, AT_SYMLINK_NOFOLLOW);
     if ((st.st_mode & S_IFMT) == S_IFREG || (st.st_mode & S_IFMT) == S_IFLNK) {
       // Is a regular file or a symbolic link.
-      int len = strlen(*iter) + 1;
+      int len = strlen(file_path) + 1;
       char *filename = malloc(len);
-      strncpy(filename, *iter, len);
+      strncpy(filename, file_path, len);
       if (simple_archiver_hash_map_get(hash_map, filename, len - 1) == NULL) {
         SDArchiverFileInfo *file_info = malloc(sizeof(SDArchiverFileInfo));
         file_info->filename = filename;
@@ -405,7 +464,7 @@ SDArchiverLinkedList *simple_archiver_parsed_to_filenames(
       // Is a directory.
       __attribute__((cleanup(simple_archiver_list_free)))
       SDArchiverLinkedList *dir_list = simple_archiver_list_init();
-      simple_archiver_list_add(dir_list, *iter, container_no_free_fn);
+      simple_archiver_list_add(dir_list, file_path, container_no_free_fn);
       char *next;
       while (dir_list->count != 0) {
         simple_archiver_list_get(dir_list, list_get_last_fn, &next);
@@ -428,7 +487,8 @@ SDArchiverLinkedList *simple_archiver_parsed_to_filenames(
             snprintf(combined_path, combined_size, "%s/%s", next,
                      dir_entry->d_name);
             unsigned int valid_idx =
-                simple_archiver_parser_internal_filename_idx(combined_path);
+                simple_archiver_parser_internal_get_first_non_current_idx(
+                    combined_path);
             if (valid_idx > 0) {
               char *new_path = malloc(combined_size - valid_idx);
               strncpy(new_path, combined_path + valid_idx,
@@ -497,12 +557,14 @@ SDArchiverLinkedList *simple_archiver_parsed_to_filenames(
   }
 #endif
 
-  // Remove leading "./" entries from files_list.
   for (SDArchiverLLNode *iter = files_list->head->next;
        iter != files_list->tail; iter = iter->next) {
     SDArchiverFileInfo *file_info = iter->data;
+
+    // Remove leading "./" entries from files_list.
     unsigned int idx =
-        simple_archiver_parser_internal_filename_idx(file_info->filename);
+        simple_archiver_parser_internal_get_first_non_current_idx(
+            file_info->filename);
     if (idx > 0) {
       int len = strlen(file_info->filename) + 1 - idx;
       char *substr = malloc(len);
@@ -510,6 +572,28 @@ SDArchiverLinkedList *simple_archiver_parsed_to_filenames(
       free(file_info->filename);
       file_info->filename = substr;
     }
+
+    // Remove "./" entries inside the file path.
+    int slash_found = 0;
+    int dot_found = 0;
+    for (idx = strlen(file_info->filename); idx-- > 0;) {
+      if (file_info->filename[idx] == '/') {
+        if (dot_found) {
+          char *temp = simple_archiver_helper_cut_substr(file_info->filename,
+                                                         idx + 1, idx + 3);
+          free(file_info->filename);
+          file_info->filename = temp;
+        } else {
+          slash_found = 1;
+          continue;
+        }
+      } else if (file_info->filename[idx] == '.' && slash_found) {
+        dot_found = 1;
+        continue;
+      }
+      slash_found = 0;
+      dot_found = 0;
+    }
   }
 
   return files_list;
index 9ba42a7027de1dca23d67be4031c2c56dad2ca63..465c363c5217f454a38238ff2b17823e3d5621d6 100644 (file)
@@ -45,6 +45,8 @@ typedef struct SDArchiverParsed {
   /// Determines where to place temporary files. If NULL, temporary files are
   /// created in the current working directory.
   const char *temp_dir;
+  /// Dir specified by "-C".
+  const char *user_cwd;
 } SDArchiverParsed;
 
 typedef struct SDArchiverFileInfo {
index b2ea7ba87667e4823ae4fe54f74353c50bf13a02..3b4f5887d56dbf7d53bc9f338a1a04d5925c56a0 100644 (file)
@@ -21,6 +21,7 @@
 
 #include "parser.h"
 
-unsigned int simple_archiver_parser_internal_filename_idx(const char *filename);
+unsigned int simple_archiver_parser_internal_get_first_non_current_idx(
+    const char *filename);
 
 #endif
index 09a1f9af44f63719f13a7bb0fb9f198f69a1f5a1..25c5890ea4c3514dd0c850a3ef54306ffd48af9b 100644 (file)
@@ -57,34 +57,42 @@ static int checks_passed = 0;
 int main(void) {
   // Test parser.
   {
-    unsigned int idx = simple_archiver_parser_internal_filename_idx("test");
+    unsigned int idx =
+        simple_archiver_parser_internal_get_first_non_current_idx("test");
     CHECK_TRUE(idx == 0);
 
-    idx = simple_archiver_parser_internal_filename_idx("./test");
+    idx = simple_archiver_parser_internal_get_first_non_current_idx("./test");
     CHECK_TRUE(idx == 2);
 
-    idx = simple_archiver_parser_internal_filename_idx("././test");
+    idx = simple_archiver_parser_internal_get_first_non_current_idx("././test");
     CHECK_TRUE(idx == 4);
 
-    idx = simple_archiver_parser_internal_filename_idx("././//././//./test");
+    idx = simple_archiver_parser_internal_get_first_non_current_idx(
+        "././//././//./test");
     CHECK_TRUE(idx == 14);
 
-    idx = simple_archiver_parser_internal_filename_idx("/././//././//./test");
+    idx = simple_archiver_parser_internal_get_first_non_current_idx(
+        "/././//././//./test");
     CHECK_TRUE(idx == 0);
 
-    idx = simple_archiver_parser_internal_filename_idx(".derp/.//././//./test");
+    idx = simple_archiver_parser_internal_get_first_non_current_idx(
+        ".derp/.//././//./test");
     CHECK_TRUE(idx == 0);
 
-    idx = simple_archiver_parser_internal_filename_idx("././/.derp/.///./test");
+    idx = simple_archiver_parser_internal_get_first_non_current_idx(
+        "././/.derp/.///./test");
     CHECK_TRUE(idx == 5);
 
-    idx = simple_archiver_parser_internal_filename_idx("././/.//.//./");
+    idx = simple_archiver_parser_internal_get_first_non_current_idx(
+        "././/.//.//./");
     CHECK_TRUE(idx == 11);
 
-    idx = simple_archiver_parser_internal_filename_idx("././/.//.//.");
+    idx = simple_archiver_parser_internal_get_first_non_current_idx(
+        "././/.//.//.");
     CHECK_TRUE(idx == 11);
 
-    idx = simple_archiver_parser_internal_filename_idx("././/.//.//");
+    idx = simple_archiver_parser_internal_get_first_non_current_idx(
+        "././/.//.//");
     CHECK_TRUE(idx == 8);
 
     SDArchiverParsed parsed = simple_archiver_create_parsed();
@@ -197,6 +205,39 @@ int main(void) {
     simple_archiver_helper_cmd_string_argv_free(result_argv);
   } while (0);
 
+  // Test helpers cut substr.
+  {
+    const char *s = "one two three.";
+    unsigned int s_len = strlen(s);
+    // Invalid range.
+    char *out = simple_archiver_helper_cut_substr(s, 1, 0);
+    CHECK_FALSE(out);
+    // First idx out of range.
+    out = simple_archiver_helper_cut_substr(s, s_len, s_len + 1);
+    CHECK_FALSE(out);
+    // Second idx out of range.
+    out = simple_archiver_helper_cut_substr(s, 1, s_len + 1);
+    CHECK_FALSE(out);
+    // Invalid cut of full string.
+    out = simple_archiver_helper_cut_substr(s, 0, s_len);
+    CHECK_FALSE(out);
+    // Cut end of string.
+    out = simple_archiver_helper_cut_substr(s, 2, s_len);
+    CHECK_TRUE(out);
+    CHECK_STREQ(out, "on");
+    free(out);
+    // Cut start of string.
+    out = simple_archiver_helper_cut_substr(s, 0, s_len - 3);
+    CHECK_TRUE(out);
+    CHECK_STREQ(out, "ee.");
+    free(out);
+    // Cut inside string.
+    out = simple_archiver_helper_cut_substr(s, 4, 8);
+    CHECK_TRUE(out);
+    CHECK_STREQ(out, "one three.");
+    free(out);
+  }
+
   printf("Checks checked: %u\n", checks_checked);
   printf("Checks passed:  %u\n", checks_passed);
   return checks_passed == checks_checked ? 0 : 1;