]> git.seodisparate.com - SimpleArchiver/commitdiff
WIP --prefix: Impl --prefix archive file fmt ver 2
authorStephen Seo <seo.disparate@gmail.com>
Mon, 27 Jan 2025 08:36:59 +0000 (17:36 +0900)
committerStephen Seo <seo.disparate@gmail.com>
Mon, 27 Jan 2025 09:21:58 +0000 (18:21 +0900)
TODO:
    Impl --prefix extract for file format version 2
    Impl --prefix archive/extract for file format version 3

src/archiver.c

index 693ad196256f96077cb3ea293e4c14e38bec6490..ea87a9e2a2fdbdc354983ecd880051222941e307 100644 (file)
@@ -1873,7 +1873,7 @@ void simple_archiver_internal_paths_to_files_map(SDArchiverHashMap *files_map,
   }
 }
 
-int internal_write_dir_entries(void *data, void *ud) {
+int internal_write_dir_entries_v2_v3(void *data, void *ud) {
   const char *dir = data;
   void **ptrs = ud;
   FILE *out_f = ptrs[0];
@@ -1881,13 +1881,18 @@ int internal_write_dir_entries(void *data, void *ud) {
 
   fprintf(stderr, "  %s\n", dir);
 
-  unsigned long dir_name_length = strlen(dir);
-  if (dir_name_length > 0xFFFF) {
+  const size_t prefix_length = state->parsed->prefix
+                               ? strlen(state->parsed->prefix)
+                               : 0;
+
+  const size_t dir_name_length = strlen(dir);
+  size_t total_name_length = dir_name_length + prefix_length;
+  if (total_name_length >= 0xFFFF) {
     fprintf(stderr, "ERROR: Dirname \"%s\" is too long!\n", dir);
     return 1;
   }
 
-  uint16_t u16 = (uint16_t)dir_name_length;
+  uint16_t u16 = (uint16_t)total_name_length;
 
   simple_archiver_helper_16_bit_be(&u16);
   if (fwrite(&u16, 2, 1, out_f) != 1) {
@@ -1895,7 +1900,22 @@ int internal_write_dir_entries(void *data, void *ud) {
     return 1;
   }
 
-  if (fwrite(dir, 1, dir_name_length + 1, out_f) != dir_name_length + 1) {
+  if (state->parsed->prefix) {
+    if (fwrite(state->parsed->prefix, 1, prefix_length, out_f)
+        != prefix_length) {
+      fprintf(stderr,
+              "ERROR: Failed to write prefix part of dirname \"%s\"!\n",
+              dir);
+      return 1;
+    } else if (fwrite(dir, 1, dir_name_length + 1, out_f)
+               != dir_name_length + 1) {
+      fprintf(stderr,
+              "ERROR: Failed to write (after prefix) dirname for \"%s\"!\n",
+              dir);
+      return 1;
+    }
+  } else if (fwrite(dir, 1, dir_name_length + 1, out_f)
+             != dir_name_length + 1) {
     fprintf(stderr, "ERROR: Failed to write dirname for \"%s\"!\n", dir);
     return 1;
   }
@@ -3444,6 +3464,10 @@ int simple_archiver_write_v2(FILE *out_f, SDArchiverState *state,
     return SDAS_FAILED_TO_WRITE;
   }
 
+  const size_t prefix_length = state->parsed->prefix
+                               ? strlen(state->parsed->prefix)
+                               : 0;
+
   char buf[SIMPLE_ARCHIVER_BUFFER_SIZE];
   uint16_t u16 = 2;
 
@@ -3682,7 +3706,11 @@ int simple_archiver_write_v2(FILE *out_f, SDArchiverState *state,
         return SDAS_FAILED_TO_WRITE;
       }
 
-      size_t len = strlen(node->data);
+      const size_t link_length = strlen(node->data);
+      size_t len = link_length;
+      if (state->parsed->prefix) {
+        len += prefix_length;
+      }
       if (len >= 0xFFFF) {
         fprintf(stderr, "ERROR: Link name is too long!\n");
         return SDAS_INVALID_PARSED_STATE;
@@ -3694,26 +3722,64 @@ int simple_archiver_write_v2(FILE *out_f, SDArchiverState *state,
         return SDAS_FAILED_TO_WRITE;
       }
       simple_archiver_helper_16_bit_be(&u16);
-      if (fwrite(node->data, 1, u16 + 1, out_f) != (size_t)u16 + 1) {
+      if (state->parsed->prefix) {
+        size_t fwrite_ret = fwrite(state->parsed->prefix,
+                                   1,
+                                   prefix_length,
+                                   out_f);
+        fwrite_ret += fwrite(node->data, 1, link_length + 1, out_f);
+        if (fwrite_ret != (size_t)u16 + 1) {
+          return SDAS_FAILED_TO_WRITE;
+        }
+      } else if (fwrite(node->data, 1, u16 + 1, out_f) != (size_t)u16 + 1) {
         return SDAS_FAILED_TO_WRITE;
       }
 
       if (abs_path && (state->parsed->flags & 0x20) == 0 && !is_invalid) {
-        len = strlen(abs_path);
-        if (len >= 0xFFFF) {
-          fprintf(stderr,
-                  "ERROR: Symlink destination absolute path is too long!\n");
-          return SDAS_INVALID_PARSED_STATE;
-        }
+        if (state->parsed->prefix) {
+          __attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
+          char *abs_path_prefixed =
+            simple_archiver_helper_insert_prefix_in_link_path(
+              state->parsed->prefix, node->data, abs_path);
+          if (!abs_path_prefixed) {
+            fprintf(stderr,
+                    "ERROR: Failed to add prefix to abs symlink!\n");
+            return SDAS_INTERNAL_ERROR;
+          }
+          const size_t abs_path_pref_length = strlen(abs_path_prefixed);
+          if (abs_path_pref_length >= 0xFFFF) {
+            fprintf(stderr,
+                    "ERROR: Symlink destination absolute path with prefix is "
+                    "too long!\n");
+            return SDAS_INVALID_PARSED_STATE;
+          }
 
-        u16 = (uint16_t)len;
-        simple_archiver_helper_16_bit_be(&u16);
-        if (fwrite(&u16, 2, 1, out_f) != 1) {
-          return SDAS_FAILED_TO_WRITE;
-        }
-        simple_archiver_helper_16_bit_be(&u16);
-        if (fwrite(abs_path, 1, u16 + 1, out_f) != (size_t)u16 + 1) {
-          return SDAS_FAILED_TO_WRITE;
+          u16 = (uint16_t)abs_path_pref_length;
+          simple_archiver_helper_16_bit_be(&u16);
+          if (fwrite(&u16, 2, 1, out_f) != 1) {
+            return SDAS_FAILED_TO_WRITE;
+          }
+          simple_archiver_helper_16_bit_be(&u16);
+          if (fwrite(abs_path_prefixed, 1, u16 + 1, out_f) != (size_t)u16 + 1) {
+            return SDAS_FAILED_TO_WRITE;
+          }
+        } else {
+          const size_t abs_path_length = strlen(abs_path);
+          if (abs_path_length >= 0xFFFF) {
+            fprintf(stderr,
+                    "ERROR: Symlink destination absolute path is too long!\n");
+            return SDAS_INVALID_PARSED_STATE;
+          }
+
+          u16 = (uint16_t)abs_path_length;
+          simple_archiver_helper_16_bit_be(&u16);
+          if (fwrite(&u16, 2, 1, out_f) != 1) {
+            return SDAS_FAILED_TO_WRITE;
+          }
+          simple_archiver_helper_16_bit_be(&u16);
+          if (fwrite(abs_path, 1, u16 + 1, out_f) != (size_t)u16 + 1) {
+            return SDAS_FAILED_TO_WRITE;
+          }
         }
       } else {
         u16 = 0;
@@ -3723,21 +3789,50 @@ int simple_archiver_write_v2(FILE *out_f, SDArchiverState *state,
       }
 
       if (rel_path && !is_invalid) {
-        len = strlen(rel_path);
-        if (len >= 0xFFFF) {
-          fprintf(stderr,
-                  "ERROR: Symlink destination relative path is too long!\n");
-          return SDAS_INVALID_PARSED_STATE;
-        }
+        if (state->parsed->prefix) {
+          __attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
+          char *rel_path_prefixed =
+            simple_archiver_helper_insert_prefix_in_link_path(
+              state->parsed->prefix, node->data, rel_path);
+          if (!rel_path_prefixed) {
+            fprintf(stderr,
+                    "ERROR: Failed to add prefix to relative symlink!\n");
+            return SDAS_INTERNAL_ERROR;
+          }
+          const size_t rel_path_pref_length = strlen(rel_path_prefixed);
+          if (rel_path_pref_length >= 0xFFFF) {
+            fprintf(stderr,
+                    "ERROR: Symlink destination relative path with prefix is "
+                    "too long!\n");
+            return SDAS_INVALID_PARSED_STATE;
+          }
 
-        u16 = (uint16_t)len;
-        simple_archiver_helper_16_bit_be(&u16);
-        if (fwrite(&u16, 2, 1, out_f) != 1) {
-          return SDAS_FAILED_TO_WRITE;
-        }
-        simple_archiver_helper_16_bit_be(&u16);
-        if (fwrite(rel_path, 1, u16 + 1, out_f) != (size_t)u16 + 1) {
-          return SDAS_FAILED_TO_WRITE;
+          u16 = (uint16_t)rel_path_pref_length;
+          simple_archiver_helper_16_bit_be(&u16);
+          if (fwrite(&u16, 2, 1, out_f) != 1) {
+            return SDAS_FAILED_TO_WRITE;
+          }
+          simple_archiver_helper_16_bit_be(&u16);
+          if (fwrite(rel_path_prefixed, 1, u16 + 1, out_f) != (size_t)u16 + 1) {
+            return SDAS_FAILED_TO_WRITE;
+          }
+        } else {
+          len = strlen(rel_path);
+          if (len >= 0xFFFF) {
+            fprintf(stderr,
+                    "ERROR: Symlink destination relative path is too long!\n");
+            return SDAS_INVALID_PARSED_STATE;
+          }
+
+          u16 = (uint16_t)len;
+          simple_archiver_helper_16_bit_be(&u16);
+          if (fwrite(&u16, 2, 1, out_f) != 1) {
+            return SDAS_FAILED_TO_WRITE;
+          }
+          simple_archiver_helper_16_bit_be(&u16);
+          if (fwrite(rel_path, 1, u16 + 1, out_f) != (size_t)u16 + 1) {
+            return SDAS_FAILED_TO_WRITE;
+          }
         }
       } else {
         u16 = 0;
@@ -3859,21 +3954,47 @@ int simple_archiver_write_v2(FILE *out_f, SDArchiverState *state,
       if (non_c_chunk_size) {
         *non_c_chunk_size += file_info_struct->file_size;
       }
-      size_t len = strlen(file_info_struct->filename);
-      if (len >= 0xFFFF) {
-        fprintf(stderr, "ERROR: Filename is too large!\n");
-        return SDAS_INVALID_FILE;
-      }
-      u16 = (uint16_t)len;
-      simple_archiver_helper_16_bit_be(&u16);
-      if (fwrite(&u16, 2, 1, out_f) != 1) {
-        return SDAS_FAILED_TO_WRITE;
+      const size_t filename_len = strlen(file_info_struct->filename);
+      if (state->parsed->prefix) {
+        const size_t total_length = filename_len + prefix_length;
+        if (total_length >= 0xFFFF) {
+          fprintf(stderr, "ERROR: Filename with prefix is too large!\n");
+          return SDAS_INVALID_FILE;
+        }
+        u16 = (uint16_t)total_length;
+        simple_archiver_helper_16_bit_be(&u16);
+        if (fwrite(&u16, 2, 1, out_f) != 1) {
+          return SDAS_FAILED_TO_WRITE;
+        }
+        simple_archiver_helper_16_bit_be(&u16);
+        if (fwrite(state->parsed->prefix, 1, prefix_length, out_f)
+            != prefix_length) {
+          return SDAS_FAILED_TO_WRITE;
+        } else if (fwrite(file_info_struct->filename,
+                          1,
+                          filename_len + 1,
+                          out_f)
+                     != filename_len + 1) {
+          return SDAS_FAILED_TO_WRITE;
+        }
+      } else {
+        if (filename_len >= 0xFFFF) {
+          fprintf(stderr, "ERROR: Filename is too large!\n");
+          return SDAS_INVALID_FILE;
+        }
+        u16 = (uint16_t)filename_len;
+        simple_archiver_helper_16_bit_be(&u16);
+        if (fwrite(&u16, 2, 1, out_f) != 1) {
+          return SDAS_FAILED_TO_WRITE;
+        }
+        simple_archiver_helper_16_bit_be(&u16);
+        if (fwrite(file_info_struct->filename, 1, u16 + 1, out_f) !=
+            (size_t)u16 + 1) {
+          return SDAS_FAILED_TO_WRITE;
+        }
       }
-      simple_archiver_helper_16_bit_be(&u16);
-      if (fwrite(file_info_struct->filename, 1, u16 + 1, out_f) !=
-          (size_t)u16 + 1) {
-        return SDAS_FAILED_TO_WRITE;
-      } else if (fwrite(file_info_struct->bit_flags, 1, 4, out_f) != 4) {
+
+      if (fwrite(file_info_struct->bit_flags, 1, 4, out_f) != 4) {
         return SDAS_FAILED_TO_WRITE;
       }
       // UID and GID.
@@ -4302,7 +4423,7 @@ int simple_archiver_write_v2(FILE *out_f, SDArchiverState *state,
   void_ptrs[1] = state;
 
   if (simple_archiver_list_get(dirs_list,
-                               internal_write_dir_entries,
+                               internal_write_dir_entries_v2_v3,
                                void_ptrs)) {
     free(void_ptrs);
     return SDAS_INTERNAL_ERROR;
@@ -5474,7 +5595,7 @@ int simple_archiver_write_v3(FILE *out_f, SDArchiverState *state,
   void_ptrs[1] = state;
 
   if (simple_archiver_list_get(dirs_list,
-                               internal_write_dir_entries,
+                               internal_write_dir_entries_v2_v3,
                                void_ptrs)) {
     free(void_ptrs);
     return SDAS_INTERNAL_ERROR;