Impl. "-C <dir>", refactorings
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 5s

This commit is contained in:
Stephen Seo 2024-07-26 12:39:56 +09:00
parent b96d26de81
commit d1609849a1
8 changed files with 210 additions and 27 deletions

View file

@ -875,7 +875,9 @@ void cleanup_free_fn(void *data) { free(data); }
int filenames_to_abs_map_fn(void *data, void *ud) { int filenames_to_abs_map_fn(void *data, void *ud) {
SDArchiverFileInfo *file_info = data; 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. // Get combined full path to file.
char *fullpath = filename_to_absolute_path(file_info->filename); 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 || \ #if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN || \
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC || \ SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC || \
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX 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 #endif
if (!cwd_dirname) { if (!cwd_dirname) {
return 1; return 1;
} }
// fprintf(stderr, "cwd_dirname: %s\n", (char*)cwd_dirname);
// Use copy of fullpath to avoid clobbering it. // Use copy of fullpath to avoid clobbering it.
__attribute__((cleanup(free_malloced_memory))) void *fullpath_copy = __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)"; return "Failed to create set of filenames (internal error)";
case SDAS_FAILED_TO_EXTRACT_SYMLINK: case SDAS_FAILED_TO_EXTRACT_SYMLINK:
return "Failed to extract symlink (internal error)"; return "Failed to extract symlink (internal error)";
case SDAS_FAILED_TO_CHANGE_CWD:
return "Failed to change current working directory";
default: default:
return "Unknown error"; 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. // First create a "set" of absolute paths to given filenames.
__attribute__((cleanup(simple_archiver_hash_map_free))) __attribute__((cleanup(simple_archiver_hash_map_free)))
SDArchiverHashMap *abs_filenames = simple_archiver_hash_map_init(); SDArchiverHashMap *abs_filenames = simple_archiver_hash_map_init();
if (simple_archiver_list_get(filenames, filenames_to_abs_map_fn, void **ptr_array = malloc(sizeof(void *) * 2);
&abs_filenames)) { 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; return SDAS_FAILED_TO_CREATE_MAP;
} }
free(ptr_array);
if (fwrite("SIMPLE_ARCHIVE_VER", 1, 18, out_f) != 18) { if (fwrite("SIMPLE_ARCHIVE_VER", 1, 18, out_f) != 18) {
return SDAS_FAILED_TO_WRITE; 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; 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; __attribute__((cleanup(free_malloced_memory))) void *decompressor_cmd = NULL;
if ((buf[0] & 1) != 0) { if ((buf[0] & 1) != 0) {

View file

@ -46,7 +46,8 @@ enum SDArchiverStateReturns {
SDAS_INVALID_FILE, SDAS_INVALID_FILE,
SDAS_INTERNAL_ERROR, SDAS_INTERNAL_ERROR,
SDAS_FAILED_TO_CREATE_MAP, 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. /// Returned pointer must not be freed.

View file

@ -191,3 +191,28 @@ int simple_archiver_helper_make_dirs(const char *file_path) {
return 1; return 1;
#endif #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;
}
}

View file

@ -46,4 +46,10 @@ void simple_archiver_helper_cmd_string_argv_free_ptr(char ***argv_strs);
/// Returns zero on success. /// Returns zero on success.
int simple_archiver_helper_make_dirs(const char *file_path); 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 #endif

View file

@ -39,7 +39,7 @@
#include "parser_internal.h" #include "parser_internal.h"
/// Gets the first non "./"-like character in the filename. /// 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) { const char *filename) {
unsigned int idx = 0; unsigned int idx = 0;
unsigned int known_good_idx = 0; unsigned int known_good_idx = 0;
@ -142,6 +142,10 @@ void simple_archiver_print_usage(void) {
fprintf(stderr, fprintf(stderr,
" Use \"-f -\" to work on stdout when creating archive or stdin " " Use \"-f -\" to work on stdout when creating archive or stdin "
"when reading archive\n"); "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, fprintf(stderr,
"--compressor <full_compress_cmd> : requires --decompressor\n"); "--compressor <full_compress_cmd> : requires --decompressor\n");
fprintf(stderr, fprintf(stderr,
@ -175,6 +179,7 @@ SDArchiverParsed simple_archiver_create_parsed(void) {
parsed.decompressor = NULL; parsed.decompressor = NULL;
parsed.working_files = NULL; parsed.working_files = NULL;
parsed.temp_dir = NULL; parsed.temp_dir = NULL;
parsed.user_cwd = NULL;
return parsed; return parsed;
} }
@ -239,6 +244,15 @@ int simple_archiver_parse_args(int argc, const char **argv,
} }
--argc; --argc;
++argv; ++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) { } else if (strcmp(argv[0], "--compressor") == 0) {
if (argc < 2) { if (argc < 2) {
fprintf(stderr, "--compressor specfied but missing argument!\n"); 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) { if (out->working_files == NULL) {
out->working_files = malloc(sizeof(char *) * 2); out->working_files = malloc(sizeof(char *) * 2);
unsigned int arg_idx = unsigned int arg_idx =
simple_archiver_parser_internal_filename_idx(argv[0]); simple_archiver_parser_internal_get_first_non_current_idx(argv[0]);
int arg_length = strlen(argv[0] + arg_idx) + 1; unsigned int arg_length = strlen(argv[0] + arg_idx) + 1;
out->working_files[0] = malloc(arg_length); out->working_files[0] = malloc(arg_length);
strncpy(out->working_files[0], argv[0] + arg_idx, arg_length); strncpy(out->working_files[0], argv[0] + arg_idx, arg_length);
simple_archiver_parser_internal_remove_end_slash(out->working_files[0]); 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; out->working_files[1] = NULL;
} else { } else {
int working_size = 1; 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. // Set new actual last element to NULL.
out->working_files[working_size] = NULL; out->working_files[working_size] = NULL;
unsigned int arg_idx = 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; int size = strlen(argv[0] + arg_idx) + 1;
// Set last element to the arg. // Set last element to the arg.
out->working_files[working_size - 1] = malloc(size); out->working_files[working_size - 1] = malloc(size);
strncpy(out->working_files[working_size - 1], argv[0] + arg_idx, size); strncpy(out->working_files[working_size - 1], argv[0] + arg_idx, size);
simple_archiver_parser_internal_remove_end_slash( simple_archiver_parser_internal_remove_end_slash(
out->working_files[working_size - 1]); 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) { for (char **iter = parsed->working_files; iter && *iter; ++iter) {
struct stat st; struct stat st;
memset(&st, 0, sizeof(struct stat)); 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) { if ((st.st_mode & S_IFMT) == S_IFREG || (st.st_mode & S_IFMT) == S_IFLNK) {
// Is a regular file or a symbolic link. // Is a regular file or a symbolic link.
int len = strlen(*iter) + 1; int len = strlen(file_path) + 1;
char *filename = malloc(len); 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) { if (simple_archiver_hash_map_get(hash_map, filename, len - 1) == NULL) {
SDArchiverFileInfo *file_info = malloc(sizeof(SDArchiverFileInfo)); SDArchiverFileInfo *file_info = malloc(sizeof(SDArchiverFileInfo));
file_info->filename = filename; file_info->filename = filename;
@ -405,7 +464,7 @@ SDArchiverLinkedList *simple_archiver_parsed_to_filenames(
// Is a directory. // Is a directory.
__attribute__((cleanup(simple_archiver_list_free))) __attribute__((cleanup(simple_archiver_list_free)))
SDArchiverLinkedList *dir_list = simple_archiver_list_init(); 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; char *next;
while (dir_list->count != 0) { while (dir_list->count != 0) {
simple_archiver_list_get(dir_list, list_get_last_fn, &next); 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, snprintf(combined_path, combined_size, "%s/%s", next,
dir_entry->d_name); dir_entry->d_name);
unsigned int valid_idx = 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) { if (valid_idx > 0) {
char *new_path = malloc(combined_size - valid_idx); char *new_path = malloc(combined_size - valid_idx);
strncpy(new_path, combined_path + valid_idx, strncpy(new_path, combined_path + valid_idx,
@ -497,12 +557,14 @@ SDArchiverLinkedList *simple_archiver_parsed_to_filenames(
} }
#endif #endif
// Remove leading "./" entries from files_list.
for (SDArchiverLLNode *iter = files_list->head->next; for (SDArchiverLLNode *iter = files_list->head->next;
iter != files_list->tail; iter = iter->next) { iter != files_list->tail; iter = iter->next) {
SDArchiverFileInfo *file_info = iter->data; SDArchiverFileInfo *file_info = iter->data;
// Remove leading "./" entries from files_list.
unsigned int idx = 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) { if (idx > 0) {
int len = strlen(file_info->filename) + 1 - idx; int len = strlen(file_info->filename) + 1 - idx;
char *substr = malloc(len); char *substr = malloc(len);
@ -510,6 +572,28 @@ SDArchiverLinkedList *simple_archiver_parsed_to_filenames(
free(file_info->filename); free(file_info->filename);
file_info->filename = substr; 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; return files_list;

View file

@ -45,6 +45,8 @@ typedef struct SDArchiverParsed {
/// Determines where to place temporary files. If NULL, temporary files are /// Determines where to place temporary files. If NULL, temporary files are
/// created in the current working directory. /// created in the current working directory.
const char *temp_dir; const char *temp_dir;
/// Dir specified by "-C".
const char *user_cwd;
} SDArchiverParsed; } SDArchiverParsed;
typedef struct SDArchiverFileInfo { typedef struct SDArchiverFileInfo {

View file

@ -21,6 +21,7 @@
#include "parser.h" #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 #endif

View file

@ -57,34 +57,42 @@ static int checks_passed = 0;
int main(void) { int main(void) {
// Test parser. // 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); CHECK_TRUE(idx == 8);
SDArchiverParsed parsed = simple_archiver_create_parsed(); SDArchiverParsed parsed = simple_archiver_create_parsed();
@ -197,6 +205,39 @@ int main(void) {
simple_archiver_helper_cmd_string_argv_free(result_argv); simple_archiver_helper_cmd_string_argv_free(result_argv);
} while (0); } 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 checked: %u\n", checks_checked);
printf("Checks passed: %u\n", checks_passed); printf("Checks passed: %u\n", checks_passed);
return checks_passed == checks_checked ? 0 : 1; return checks_passed == checks_checked ? 0 : 1;