]> git.seodisparate.com - SimpleArchiver/commitdiff
Some work on arg parser
authorStephen Seo <seo.disparate@gmail.com>
Mon, 1 Jul 2024 06:13:06 +0000 (15:13 +0900)
committerStephen Seo <seo.disparate@gmail.com>
Mon, 1 Jul 2024 06:13:06 +0000 (15:13 +0900)
Also added unit tests for arg parser.

.forgejo/workflows/unittest.yml
CMakeLists.txt
cosmopolitan/Makefile
src/parser.c
src/parser_internal.h [new file with mode: 0644]
src/test.c [new file with mode: 0644]

index 95069a83756fe574557c2bdd20e21077d31b1ec3..42cebc5099f38e995f882a653e8c0dcca590e6a5 100644 (file)
@@ -18,3 +18,5 @@ jobs:
         name: Build
       - run: ./buildDebug/test_datastructures
         name: Run test_datastructures
+      - run: ./buildDebug/test_simplearchiver
+        name: Run test_simplearchiver
index 3542f022ed660ed0e3eb9cb31df4cc16ee920914..59396030d8a374ca64084da766af4c190fcfa240 100644 (file)
@@ -33,3 +33,8 @@ add_executable(test_datastructures
     src/data_structures/priority_heap.c
     src/algorithms/linear_congruential_gen.c
 )
+
+add_executable(test_simplearchiver
+    src/test.c
+    src/parser.c
+)
index 098c0a363d60b33c1982049bdb4bd5f6a5226845..25b01881277143490062f175d3baac548a58e919 100644 (file)
@@ -13,6 +13,7 @@ SOURCES = \
 
 HEADERS = \
                ../src/parser.h \
+               ../src/parser_internal.h \
                ../src/algorithms/linear_congruential_gen.h \
                ../src/data_structures/linked_list.h \
                ../src/data_structures/hash_map.h \
index fbe91c7f941c7a558affe684f8e5288dad070a3c..0a31d3fb78ec70a907c33b98b1e434be2d5b758c 100644 (file)
 #include <stdlib.h>
 #include <string.h>
 
+#include "parser_internal.h"
+
+/// Gets the first non "./"-like character in the filename.
+unsigned int simple_archiver_parser_internal_filename_idx(
+    const char *filename) {
+  unsigned int idx = 0;
+  unsigned int known_good_idx = 0;
+  const unsigned int length = strlen(filename);
+
+  // 0b0001 - checked that idx char is '.'
+  // 0b0010 - checked that idx char is '/'
+  unsigned int flags = 0;
+
+  for (; idx < length; ++idx) {
+    if ((flags & 3) == 0) {
+      if (filename[idx] == 0) {
+        return known_good_idx;
+      } else if (filename[idx] == '.') {
+        flags |= 1;
+      } else {
+        return idx;
+      }
+    } else if ((flags & 3) == 1) {
+      if (filename[idx] == 0) {
+        return known_good_idx;
+      } else if (filename[idx] == '/') {
+        flags |= 2;
+      } else {
+        return idx - 1;
+      }
+    } else if ((flags & 3) == 3) {
+      if (filename[idx] == 0) {
+        return known_good_idx;
+      } else if (filename[idx] == '/') {
+        continue;
+      } else if (filename[idx] == '.') {
+        flags &= 0xFFFFFFFC;
+        known_good_idx = idx;
+        --idx;
+        continue;
+      } else {
+        break;
+      }
+    }
+  }
+
+  if (filename[idx] == 0) {
+    return known_good_idx;
+  }
+
+  return idx;
+}
+
 void simple_archiver_print_usage(void) {
   puts("Usage flags:");
   puts("-c : create archive file");
@@ -29,6 +82,7 @@ void simple_archiver_print_usage(void) {
   puts("-f <filename> : filename to work on");
   puts("--compressor <full_compress_cmd> : requires --decompressor");
   puts("--decompressor <full_decompress_cmd> : requires --compressor");
+  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.");
 }
@@ -92,6 +146,8 @@ int simple_archiver_parse_args(int argc, const char **argv,
         strncpy(out->decompressor, argv[1], size);
         --argc;
         ++argv;
+      } else if (argv[0][0] == '-' && argv[0][1] == '-' && argv[0][2] == 0) {
+        is_remaining_args = 1;
       } else if (argv[0][0] != '-') {
         is_remaining_args = 1;
         continue;
@@ -99,9 +155,11 @@ int simple_archiver_parse_args(int argc, const char **argv,
     } else {
       if (out->working_files == NULL) {
         out->working_files = malloc(sizeof(char *) * 2);
-        int arg_length = strlen(argv[0]) + 1;
+        unsigned int arg_idx =
+            simple_archiver_parser_internal_filename_idx(argv[0]);
+        int arg_length = strlen(argv[0] + arg_idx) + 1;
         out->working_files[0] = malloc(arg_length);
-        strncpy(out->working_files[0], argv[0], arg_length);
+        strncpy(out->working_files[0], argv[0] + arg_idx, arg_length);
         out->working_files[1] = NULL;
       } else {
         int working_size = 1;
@@ -117,10 +175,12 @@ int simple_archiver_parse_args(int argc, const char **argv,
 
         // Set new actual last element to NULL.
         out->working_files[working_size] = NULL;
-        int size = strlen(argv[0]) + 1;
+        unsigned int arg_idx =
+            simple_archiver_parser_internal_filename_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], size);
+        strncpy(out->working_files[working_size - 1], argv[0] + arg_idx, size);
       }
     }
 
diff --git a/src/parser_internal.h b/src/parser_internal.h
new file mode 100644 (file)
index 0000000..b2ea7ba
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ *
+ * `parser_internal.h` is the header for parsing args with internal functions.
+ */
+
+#ifndef SEODISPARATE_COM_SIMPLE_ARCHIVER_PARSER_INTERNAL_H_
+#define SEODISPARATE_COM_SIMPLE_ARCHIVER_PARSER_INTERNAL_H_
+
+#include "parser.h"
+
+unsigned int simple_archiver_parser_internal_filename_idx(const char *filename);
+
+#endif
diff --git a/src/test.c b/src/test.c
new file mode 100644 (file)
index 0000000..cf6cb4f
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * 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.
+ *
+ * `test.c` is the source for testing code.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "parser_internal.h"
+
+static int checks_checked = 0;
+static int checks_passed = 0;
+
+#define CHECK_TRUE(x)                                             \
+  do {                                                            \
+    ++checks_checked;                                             \
+    if (!(x)) {                                                   \
+      printf("CHECK_TRUE at line %u failed: %s\n", __LINE__, #x); \
+    } else {                                                      \
+      ++checks_passed;                                            \
+    }                                                             \
+  } while (0);
+#define CHECK_FALSE(x)                                             \
+  do {                                                             \
+    ++checks_checked;                                              \
+    if (x) {                                                       \
+      printf("CHECK_FALSE at line %u failed: %s\n", __LINE__, #x); \
+    } else {                                                       \
+      ++checks_passed;                                             \
+    }                                                              \
+  } while (0);
+
+int main(void) {
+  // Test parser.
+  {
+    unsigned int idx = simple_archiver_parser_internal_filename_idx("test");
+    CHECK_TRUE(idx == 0);
+
+    idx = simple_archiver_parser_internal_filename_idx("./test");
+    CHECK_TRUE(idx == 2);
+
+    idx = simple_archiver_parser_internal_filename_idx("././test");
+    CHECK_TRUE(idx == 4);
+
+    idx = simple_archiver_parser_internal_filename_idx("././//././//./test");
+    CHECK_TRUE(idx == 14);
+
+    idx = simple_archiver_parser_internal_filename_idx("/././//././//./test");
+    CHECK_TRUE(idx == 0);
+
+    idx = simple_archiver_parser_internal_filename_idx(".derp/.//././//./test");
+    CHECK_TRUE(idx == 0);
+
+    idx = simple_archiver_parser_internal_filename_idx("././/.derp/.///./test");
+    CHECK_TRUE(idx == 5);
+
+    idx = simple_archiver_parser_internal_filename_idx("././/.//.//./");
+    CHECK_TRUE(idx == 11);
+
+    idx = simple_archiver_parser_internal_filename_idx("././/.//.//.");
+    CHECK_TRUE(idx == 11);
+
+    idx = simple_archiver_parser_internal_filename_idx("././/.//.//");
+    CHECK_TRUE(idx == 8);
+
+    SDArchiverParsed parsed = simple_archiver_create_parsed();
+    simple_archiver_parse_args(
+        4,
+        (const char *[]){"parser", "--", "././/././//./derp", "./doop", NULL},
+        &parsed);
+
+    CHECK_TRUE(strcmp("derp", parsed.working_files[0]) == 0);
+    CHECK_TRUE(strcmp("doop", parsed.working_files[1]) == 0);
+    CHECK_TRUE(parsed.working_files[2] == NULL);
+    CHECK_TRUE(parsed.filename == NULL);
+    CHECK_TRUE(parsed.flags == 0);
+
+    simple_archiver_free_parsed(&parsed);
+
+    parsed = simple_archiver_create_parsed();
+    simple_archiver_parse_args(
+        7,
+        (const char *[]){"parser", "-x", "-f", "the_filename",
+                         "././/././//./.derp", "././//./_doop",
+                         "./../../.prev_dir_file", NULL},
+        &parsed);
+
+    CHECK_TRUE(strcmp(".derp", parsed.working_files[0]) == 0);
+    CHECK_TRUE(strcmp("_doop", parsed.working_files[1]) == 0);
+    CHECK_TRUE(strcmp("../../.prev_dir_file", parsed.working_files[2]) == 0);
+    CHECK_TRUE(parsed.working_files[3] == NULL);
+    CHECK_TRUE(strcmp("the_filename", parsed.filename) == 0);
+    CHECK_TRUE(parsed.flags == 1);
+
+    simple_archiver_free_parsed(&parsed);
+  }
+
+  printf("Checks checked: %u\n", checks_checked);
+  printf("Checks passed:  %u\n", checks_passed);
+  return checks_passed == checks_checked ? 0 : 1;
+}