]> git.seodisparate.com - SimpleArchiver/commitdiff
Impl. basic functionality "MVP"
authorStephen Seo <seo.disparate@gmail.com>
Tue, 16 Jul 2024 07:16:58 +0000 (16:16 +0900)
committerStephen Seo <seo.disparate@gmail.com>
Tue, 16 Jul 2024 07:16:58 +0000 (16:16 +0900)
"MinimumViableProduct", resolves #2 .

TODO:
  Support de/compressor cmds when creating archive.
  Support symbolic links when creating archive.
  Support extracting archive.

CMakeLists.txt
cosmopolitan/Makefile
src/archiver.c [new file with mode: 0644]
src/archiver.h [new file with mode: 0644]
src/data_structures/linked_list.c
src/data_structures/linked_list.h
src/main.c
src/parser.c

index 920355470bcb68bcb0affab45e5da96c04402809..1ecbcee4f8dca0049579fc359e7328736f633f53 100644 (file)
@@ -7,6 +7,7 @@ set(SimpleArchiver_SOURCES
     src/main.c
     src/parser.c
     src/helpers.c
+    src/archiver.c
     src/data_structures/linked_list.c
     src/data_structures/hash_map.c
     src/data_structures/priority_heap.c
index 7ac2466d6462a727599ccd36920270ffea0795e4..bf753de8fad1c9b7f722163372a2cbe589d6a619 100644 (file)
@@ -7,6 +7,7 @@ SOURCES = \
                ../src/main.c \
                ../src/parser.c \
                ../src/helpers.c \
+               ../src/archiver.c \
                ../src/algorithms/linear_congruential_gen.c \
                ../src/data_structures/linked_list.c \
                ../src/data_structures/hash_map.c \
@@ -16,6 +17,7 @@ HEADERS = \
                ../src/parser.h \
                ../src/parser_internal.h \
                ../src/helpers.h \
+               ../src/archiver.h \
                ../src/algorithms/linear_congruential_gen.h \
                ../src/data_structures/linked_list.h \
                ../src/data_structures/hash_map.h \
diff --git a/src/archiver.c b/src/archiver.c
new file mode 100644 (file)
index 0000000..c1853cb
--- /dev/null
@@ -0,0 +1,271 @@
+/*
+ * Copyright 2024 Stephen Seo
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * `archiver.c` is the source for an interface to creating an archive file.
+ */
+
+#include "archiver.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "helpers.h"
+
+typedef struct SDArchiverInternalToWrite {
+  void *buf;
+  uint64_t size;
+} SDArchiverInternalToWrite;
+
+void free_FILE_helper(FILE **fd) {
+  if (fd && *fd) {
+    fclose(*fd);
+    *fd = NULL;
+  }
+}
+
+void free_internal_to_write(void *data) {
+  SDArchiverInternalToWrite *to_write = data;
+  free(to_write->buf);
+  free(data);
+}
+
+int write_list_datas_fn(void *data, void *ud) {
+  SDArchiverInternalToWrite *to_write = data;
+  FILE *out_f = ud;
+
+  fwrite(to_write->buf, 1, to_write->size, out_f);
+  return 0;
+}
+
+int write_files_fn(void *data, void *ud) {
+  const SDArchiverFileInfo *file_info = data;
+  const SDArchiverState *state = ud;
+
+  __attribute__((cleanup(simple_archiver_list_free)))
+  SDArchiverLinkedList *to_write = simple_archiver_list_init();
+  SDArchiverInternalToWrite *temp_to_write;
+
+  if (!file_info->filename) {
+    // Invalid entry, no filename.
+    return 1;
+  } else if (!state->out_f) {
+    // Invalid out-ptr.
+    return 1;
+  } else if (!file_info->link_dest) {
+    // Regular file, not a symbolic link.
+    if (state->parsed->compressor && state->parsed->decompressor) {
+      // De/compressor specified.
+      // TODO
+    } else {
+      uint16_t u16;
+      uint64_t u64;
+
+      u16 = strlen(file_info->filename);
+
+      // Write filename length.
+      simple_archiver_helper_16_bit_be(&u16);
+      temp_to_write = malloc(sizeof(SDArchiverInternalToWrite));
+      temp_to_write->buf = malloc(2);
+      temp_to_write->size = 2;
+      memcpy(temp_to_write->buf, &u16, 2);
+      simple_archiver_list_add(to_write, temp_to_write, free_internal_to_write);
+
+      // Write filename.
+      simple_archiver_helper_16_bit_be(&u16);
+      temp_to_write = malloc(sizeof(SDArchiverInternalToWrite));
+      temp_to_write->buf = malloc(u16 + 1);
+      temp_to_write->size = u16 + 1;
+      memcpy(temp_to_write->buf, file_info->filename, u16 + 1);
+      simple_archiver_list_add(to_write, temp_to_write, free_internal_to_write);
+
+      // Write flags.
+      temp_to_write = malloc(sizeof(SDArchiverInternalToWrite));
+      temp_to_write->buf = malloc(4);
+      temp_to_write->size = 4;
+      for (unsigned int idx = 0; idx < temp_to_write->size; ++idx) {
+        ((unsigned char *)temp_to_write->buf)[idx] = 0;
+      }
+      simple_archiver_list_add(to_write, temp_to_write, free_internal_to_write);
+
+      // Write file length.
+      __attribute__((cleanup(free_FILE_helper))) FILE *fd =
+          fopen(file_info->filename, "rb");
+      if (!fd) {
+        // Error.
+        return 1;
+      } else if (fseek(fd, 0, SEEK_END) != 0) {
+        // Error.
+        return 1;
+      }
+      long end = ftell(fd);
+      if (end == -1L) {
+        // Error.
+        return 1;
+      }
+      u64 = end;
+      simple_archiver_helper_64_bit_be(&u64);
+      temp_to_write = malloc(sizeof(SDArchiverInternalToWrite));
+      temp_to_write->buf = malloc(8);
+      temp_to_write->size = 8;
+      memcpy(temp_to_write->buf, &u64, 8);
+      simple_archiver_list_add(to_write, temp_to_write, free_internal_to_write);
+
+      if (fseek(fd, 0, SEEK_SET) != 0) {
+        // Error.
+        return 1;
+      }
+
+      // Write all previuosly set data before writing file.
+      simple_archiver_list_get(to_write, write_list_datas_fn, state->out_f);
+
+      simple_archiver_list_free(&to_write);
+
+      // Write file.
+      char buf[1024];
+      size_t ret;
+      do {
+        ret = fread(buf, 1, 1024, fd);
+        if (ret == 1024) {
+          fwrite(buf, 1, 1024, state->out_f);
+        } else if (ret > 0) {
+          fwrite(buf, 1, ret, state->out_f);
+        }
+        if (feof(fd)) {
+          break;
+        } else if (ferror(fd)) {
+          // Error.
+          break;
+        }
+      } while (1);
+    }
+  } else {
+    // A symblic link.
+    // TODO
+  }
+
+  return 0;
+}
+
+SDArchiverState *simple_archiver_init_state(const SDArchiverParsed *parsed) {
+  if (!parsed) {
+    return NULL;
+  }
+
+  SDArchiverState *state = malloc(sizeof(SDArchiverState));
+  state->flags = 0;
+  state->parsed = parsed;
+  state->out_f = NULL;
+
+  return state;
+}
+
+void simple_archiver_free_state(SDArchiverState **state) {
+  if (state && *state) {
+    free(*state);
+    *state = NULL;
+  }
+}
+
+int simple_archiver_write_all(FILE *out_f, SDArchiverState *state,
+                              const SDArchiverLinkedList *filenames) {
+  if (fwrite("SIMPLE_ARCHIVE_VER", 1, 18, out_f) != 18) {
+    return SDAS_FAILED_TO_WRITE;
+  }
+
+  uint16_t u16 = 0;
+
+  // No need to convert to big-endian for version 0.
+  // simple_archiver_helper_16_bit_be(&u16);
+
+  if (fwrite(&u16, 2, 1, out_f) != 1) {
+    return SDAS_FAILED_TO_WRITE;
+  }
+
+  if (state->parsed->compressor && !state->parsed->decompressor) {
+    return SDAS_NO_DECOMPRESSOR;
+  } else if (!state->parsed->compressor && state->parsed->decompressor) {
+    return SDAS_NO_COMPRESSOR;
+  } else if (state->parsed->compressor && state->parsed->decompressor) {
+    // Write the four flag bytes with first bit set.
+    unsigned char c = 1;
+    if (fwrite(&c, 1, 1, out_f) != 1) {
+      return SDAS_FAILED_TO_WRITE;
+    }
+    c = 0;
+    for (unsigned int i = 0; i < 3; ++i) {
+      if (fwrite(&c, 1, 1, out_f) != 1) {
+        return SDAS_FAILED_TO_WRITE;
+      }
+    }
+
+    // De/compressor bytes.
+    u16 = strlen(state->parsed->compressor);
+    // To big-endian.
+    simple_archiver_helper_16_bit_be(&u16);
+    // Write the size in big-endian.
+    if (fwrite(&u16, 2, 1, out_f) != 1) {
+      return SDAS_FAILED_TO_WRITE;
+    }
+    // From big-endian.
+    simple_archiver_helper_16_bit_be(&u16);
+    // Write the compressor cmd including the NULL at the end of the string.
+    if (fwrite(state->parsed->compressor, 1, u16 + 1, out_f) !=
+        (size_t)u16 + 1) {
+      return SDAS_FAILED_TO_WRITE;
+    }
+
+    u16 = strlen(state->parsed->decompressor);
+    // To big-endian.
+    simple_archiver_helper_16_bit_be(&u16);
+    // Write the size in big-endian.
+    if (fwrite(&u16, 2, 1, out_f) != 1) {
+      return SDAS_FAILED_TO_WRITE;
+    }
+    // From big-endian.
+    simple_archiver_helper_16_bit_be(&u16);
+    // Write the decompressor cmd including the NULL at the end of the string.
+    if (fwrite(state->parsed->decompressor, 1, u16 + 1, out_f) !=
+        (size_t)u16 + 1) {
+      return SDAS_FAILED_TO_WRITE;
+    }
+  } else {
+    // Write the four flag bytes with first bit NOT set.
+    unsigned char c = 0;
+    for (unsigned int i = 0; i < 4; ++i) {
+      if (fwrite(&c, 1, 1, out_f) != 1) {
+        return SDAS_FAILED_TO_WRITE;
+      }
+    }
+  }
+
+  // Write file count.
+  {
+    uint32_t u32 = filenames->count;
+    simple_archiver_helper_32_bit_be(&u32);
+    if (fwrite(&u32, 1, 4, out_f) != 4) {
+      return SDAS_FAILED_TO_WRITE;
+    }
+  }
+
+  // Iterate over files in list to write.
+  state->out_f = out_f;
+  if (simple_archiver_list_get(filenames, write_files_fn, state)) {
+    // Error occurred.
+  }
+  state->out_f = NULL;
+
+  return SDAS_SUCCESS;
+}
diff --git a/src/archiver.h b/src/archiver.h
new file mode 100644 (file)
index 0000000..4cff97d
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2024 Stephen Seo
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * `archiver.h` is the header for an interface to creating an archive file.
+ */
+
+#ifndef SEODISPARATE_COM_SIMPLE_ARCHIVER_ARCHIVER_H_
+#define SEODISPARATE_COM_SIMPLE_ARCHIVER_ARCHIVER_H_
+
+#include <stdio.h>
+
+#include "data_structures/linked_list.h"
+#include "parser.h"
+
+typedef struct SDArchiverState {
+  /*
+   */
+  unsigned int flags;
+  const SDArchiverParsed *parsed;
+  FILE *out_f;
+} SDArchiverState;
+
+enum SDArchiverStateReturns {
+  SDAS_SUCCESS = 0,
+  SDAS_HEADER_ALREADY_WRITTEN = 1,
+  SDAS_FAILED_TO_WRITE,
+  SDAS_NO_COMPRESSOR,
+  SDAS_NO_DECOMPRESSOR,
+  SDAS_INVALID_PARSED_STATE
+};
+
+SDArchiverState *simple_archiver_init_state(const SDArchiverParsed *parsed);
+void simple_archiver_free_state(SDArchiverState **state);
+
+/// Returns zero on success. Otherwise one value from SDArchiverStateReturns
+/// enum.
+int simple_archiver_write_all(FILE *out_f, SDArchiverState *state,
+                              const SDArchiverLinkedList *filenames);
+
+#endif
index c4ad0ddca771ffcf97d326e9699130e3ae1bde96..c114be7ab805fb91902ace4963eada632854c3b9 100644 (file)
@@ -178,7 +178,7 @@ int simple_archiver_list_remove_once(SDArchiverLinkedList *list,
   return 0;
 }
 
-void *simple_archiver_list_get(SDArchiverLinkedList *list,
+void *simple_archiver_list_get(const SDArchiverLinkedList *list,
                                int (*data_check_fn)(void *, void *),
                                void *user_data) {
   if (!list) {
index fbebf8edfa0f9d14d303d0fcc2ba5fd12e27f289..5b093c277caee016a3960123346896bf7dadbc14 100644 (file)
@@ -62,7 +62,7 @@ int simple_archiver_list_remove_once(SDArchiverLinkedList *list,
 /// Returns non-null on success.
 /// data_check_fn must return non-zero if the data passed to it is to be
 /// returned.
-void *simple_archiver_list_get(SDArchiverLinkedList *list,
+void *simple_archiver_list_get(const SDArchiverLinkedList *list,
                                int (*data_check_fn)(void *, void *),
                                void *user_data);
 
index da67c9d470983d681b53be7e7e52cf12e8592042..1492fa3fbf012f21fa2fe29e601c4966a0319d2d 100644 (file)
 
 #include <stdio.h>
 
+#include "archiver.h"
 #include "parser.h"
 
 int print_list_fn(void *data, __attribute__((unused)) void *ud) {
   const SDArchiverFileInfo *file_info = data;
   if (file_info->link_dest == NULL) {
-    printf("  REGULAR FILE:  %s\n", file_info->filename);
+    fprintf(stderr, "  REGULAR FILE:  %s\n", file_info->filename);
   } else {
-    printf("  SYMBOLIC LINK: %s -> %s\n", file_info->filename,
-           file_info->link_dest);
+    fprintf(stderr, "  SYMBOLIC LINK: %s -> %s\n", file_info->filename,
+            file_info->link_dest);
   }
   return 0;
 }
@@ -54,8 +55,25 @@ int main(int argc, const char **argv) {
   SDArchiverLinkedList *filenames =
       simple_archiver_parsed_to_filenames(&parsed);
 
-  puts("Filenames:");
+  fprintf(stderr, "Filenames:\n");
   simple_archiver_list_get(filenames, print_list_fn, NULL);
 
+  if ((parsed.flags & 1) == 0) {
+    FILE *file = fopen(parsed.filename, "wb");
+    if (!file) {
+      fprintf(stderr, "ERROR: Failed to open \"%s\" for writing!\n",
+              parsed.filename);
+      return 2;
+    }
+
+    __attribute__((cleanup(simple_archiver_free_state)))
+    SDArchiverState *state = simple_archiver_init_state(&parsed);
+
+    if (simple_archiver_write_all(file, state, filenames) != SDAS_SUCCESS) {
+      fprintf(stderr, "Error during writing.");
+    }
+    fclose(file);
+  }
+
   return 0;
 }
index 37e174e10a3d2be4f0da178903ae1680ddea5414..c59131ef862baa65007f2a2a96124c745011436a 100644 (file)
@@ -134,16 +134,23 @@ int list_remove_same_str_fn(void *data, void *ud) {
 }
 
 void simple_archiver_print_usage(void) {
-  puts("Usage flags:");
-  puts("-c : create archive file");
-  puts("-x : extract archive file");
-  puts("-f <filename> : filename to work on");
-  puts("--compressor <full_compress_cmd> : requires --decompressor");
-  puts("--decompressor <full_decompress_cmd> : requires --compressor");
-  puts("--overwrite-create : allows overwriting an archive file");
-  puts("-- : specifies remaining arguments are files to archive/extract");
-  puts("If creating archive file, remaining args specify files to archive.");
-  puts("If extracting archive file, remaining args specify files to extract.");
+  fprintf(stderr, "Usage flags:\n");
+  fprintf(stderr, "-c : create archive file\n");
+  fprintf(stderr, "-x : extract archive file\n");
+  fprintf(stderr, "-f <filename> : filename to work on\n");
+  fprintf(stderr,
+          "--compressor <full_compress_cmd> : requires --decompressor\n");
+  fprintf(stderr,
+          "--decompressor <full_decompress_cmd> : requires --compressor\n");
+  fprintf(stderr, "--overwrite-create : allows overwriting an archive file\n");
+  fprintf(stderr,
+          "-- : specifies remaining arguments are files to archive/extract\n");
+  fprintf(
+      stderr,
+      "If creating archive file, remaining args specify files to archive.\n");
+  fprintf(
+      stderr,
+      "If extracting archive file, remaining args specify files to extract.\n");
 }
 
 SDArchiverParsed simple_archiver_create_parsed(void) {
@@ -181,7 +188,10 @@ int simple_archiver_parse_args(int argc, const char **argv,
 
   while (argc > 0) {
     if (!is_remaining_args) {
-      if (strcmp(argv[0], "-c") == 0) {
+      if (strcmp(argv[0], "-h") == 0 || strcmp(argv[0], "--help") == 0) {
+        simple_archiver_print_usage();
+        exit(0);
+      } else if (strcmp(argv[0], "-c") == 0) {
         // unset first bit.
         out->flags &= 0xFFFFFFFE;
       } else if (strcmp(argv[0], "-x") == 0) {
@@ -348,7 +358,7 @@ SDArchiverLinkedList *simple_archiver_parsed_to_filenames(
                 strcmp(dir_entry->d_name, "..") == 0) {
               continue;
             }
-            printf("dir entry in %s is %s\n", next, dir_entry->d_name);
+            fprintf(stderr, "dir entry in %s is %s\n", next, dir_entry->d_name);
             int combined_size = strlen(next) + strlen(dir_entry->d_name) + 2;
             char *combined_path = malloc(combined_size);
             snprintf(combined_path, combined_size, "%s/%s", next,