Also added unit tests for arg parser.
name: Build
- run: ./buildDebug/test_datastructures
name: Run test_datastructures
+ - run: ./buildDebug/test_simplearchiver
+ name: Run test_simplearchiver
src/data_structures/priority_heap.c
src/algorithms/linear_congruential_gen.c
)
+
+add_executable(test_simplearchiver
+ src/test.c
+ src/parser.c
+)
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 \
#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");
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.");
}
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;
} 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;
// 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);
}
}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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;
+}