Skip to content

Commit

Permalink
refactor: add and use str concat function
Browse files Browse the repository at this point in the history
- add a function, strccat, which uses memccpy (from C23) to concatenate arrays of strs into an outgoing str. See https://developers.redhat.com/blog/2019/08/12/efficient-string-copying-and-concatenation-in-c

- when creating a temporary file name for cycle removal, use the original file's name in the tempoaray name, e.g. file1.txt -> file1.txt_mmv_XXXXXX
  • Loading branch information
mcauley-penney committed Dec 7, 2023
1 parent 6962b7c commit 485788f
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 26 deletions.
46 changes: 21 additions & 25 deletions src/mmv.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

int write_strarr_to_tmpfile(struct Set *set, char tmp_path_template[])
{
int *i;
int *i, *set_end_pos = set_end(set);

FILE *tmp_fptr = open_tmpfile_fptr(tmp_path_template);
if (tmp_fptr == NULL)
Expand All @@ -11,7 +11,7 @@ int write_strarr_to_tmpfile(struct Set *set, char tmp_path_template[])
return errno;
}

for (i = set_begin(set); i < set_end(set); i = set_next(i))
for (i = set_begin(set); i < set_end_pos; i = set_next(i))
fprintf(tmp_fptr, "%s\n", *get_set_pos(set, i));

fclose(tmp_fptr);
Expand All @@ -31,35 +31,24 @@ FILE *open_tmpfile_fptr(char *tmp_path)
return fdopen(tmp_fd, "w");
}

int edit_tmpfile(const char *path)
int edit_tmpfile(char *path)
{
int ret;
char *editor_name = getenv("EDITOR");
if (editor_name == NULL)
editor_name = "nano";

// provide space for "$EDITOR <path>\0", e.g. "nano test.txt\0"
const size_t cmd_len = strlen(editor_name) + strlen(path) + 2;

char *edit_cmd = malloc(sizeof(edit_cmd) * cmd_len);
char *cmd_parts[3] = {editor_name, " ", path};
char *edit_cmd = strccat(cmd_parts, 3);
if (edit_cmd == NULL)
{
perror("mmv: failed to allocate memory for $EDITOR command string");
return errno;
}

ret = snprintf(edit_cmd, cmd_len, "%s %s", editor_name, path);
if (ret < 0 || ret > (int)cmd_len)
{
perror("mmv: couldn't create $EDITOR command string");
free(edit_cmd);
return errno;
}

#if DEBUG == 0
if (system(edit_cmd) != 0)
{
fprintf(stderr, "mmv: \'%s\' returned non-zero exit status: %d\n", editor_name, ret);
fprintf(stderr, "mmv: \'%s\' returned non-zero exit status\n", editor_name);
free(edit_cmd);
return errno;
}
Expand Down Expand Up @@ -138,7 +127,6 @@ int rename_paths(struct Set *src_set, struct Set *dest_set, struct Opts *opts)
int *i, *j;
char *src_str, *dest_str;


for (i = set_begin(src_set), j = set_begin(dest_set); i < set_end(src_set) && j < set_end(dest_set);
i = set_next(i), j = set_next(j))
{
Expand Down Expand Up @@ -173,14 +161,14 @@ void rm_path(char *path)
fprintf(stderr, "mmv: failed to delete \'%s\': %s\n", path, strerror(errno));
}

// TODO: modularize
int rm_cycles(struct Set *src_set, struct Set *dest_set, struct Opts *opts)
{
int *i, *j, is_dupe;
int is_dupe, *i, *j, *src_end_pos = set_end(src_set), *dest_end_pos = set_end(dest_set);
unsigned long int u_key;
char *src_str, *dest_str;
char **cur_src_pos;
char *src_str, *dest_str, **cur_src_pos;

for (i = set_begin(src_set), j = set_begin(dest_set); i < set_end(src_set) && j < set_end(dest_set);
for (i = set_begin(src_set), j = set_begin(dest_set); i < src_end_pos && j < dest_end_pos;
i = set_next(i), j = set_next(j))
{
src_str = *get_set_pos(src_set, i);
Expand All @@ -190,10 +178,19 @@ int rm_cycles(struct Set *src_set, struct Set *dest_set, struct Opts *opts)
{
u_key = (unsigned int)*j;
is_dupe = is_duplicate_element(dest_str, src_set, &u_key);
char tmp_path[] = "mmv_cycle_XXXXXX";
char template[] = "_mmv_XXXXXX";

if (is_dupe == 0)
{
cur_src_pos = get_set_pos(src_set, j);
char *tmp_path_parts[2] = {*cur_src_pos, template};
char *tmp_path = strccat(tmp_path_parts, 2);
if (tmp_path == NULL)
{
perror("mmv: failed to allocate memory for cycle-removal temporary path");
return -1;
}

// create temporary name using the current name
int tmp_fd = mkstemp(tmp_path);
if (tmp_fd == -1)
Expand All @@ -202,14 +199,13 @@ int rm_cycles(struct Set *src_set, struct Set *dest_set, struct Opts *opts)
return -1;
}

cur_src_pos = get_set_pos(src_set, j);

// rename to temporary name
rename_path(*cur_src_pos, tmp_path, opts);

// update str in src map to temp_str
free(*cur_src_pos);
cpy_str_to_arr(cur_src_pos, tmp_path);
free(tmp_path);
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/mmv.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ FILE *open_tmpfile_fptr(char *tmp_path);
*
* @param path: file path to open in editor
*/
int edit_tmpfile(const char *path);
int edit_tmpfile(char *path);

struct Set *init_dest_set(unsigned int num_keys, char path[]);

Expand Down
23 changes: 23 additions & 0 deletions src/utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,26 @@ char *cpy_str_to_arr(char **arr_dest, const char *src_str)

return strcpy(*arr_dest, src_str);
}

char *strccat(char **str_arr, unsigned int num_strs)
{
if (num_strs < 1)
return NULL;

unsigned int i;
size_t size = 4200 * sizeof(char);
char *concat_str = malloc(size);

char *p = memccpy(concat_str, str_arr[0], '\0', size - 1);

for (i = 1; i < num_strs; i++)
if (p)
p = memccpy(p - 1, str_arr[i], '\0', size - (size_t)p);
else
{
free(concat_str);
return NULL;
}

return concat_str;
}
1 change: 1 addition & 0 deletions src/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@
*/
char *cpy_str_to_arr(char **array_pos, const char *src_str);

char *strccat(char **str_arr, unsigned int num_strs);

#endif // UTILS_H
41 changes: 41 additions & 0 deletions test/test_utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,50 @@ void test_cpy_str_to_arr(void)
TEST_ASSERT_EQUAL_STRING("", test_arr[0]);
}

void test_strccat_no_args(void)
{
char *parts[7] = {"This", " ", "is", " ", "a", " ", "test"};

char *test_str = strccat(parts, 0);

TEST_ASSERT_NULL(test_str);
}

void test_strccat_incomplete_args(void)
{
char *parts[7] = {"This", " ", "is", " ", "a", " ", "test"};

char *test_str = strccat(parts, 3);

TEST_ASSERT_EQUAL_STRING("This is", test_str);
}

void test_strccat_excess_args(void)
{
char *parts[7] = {"This", " ", "is", " ", "a", " ", "test"};

char *test_str = strccat(parts, 8);

TEST_ASSERT_EQUAL_STRING("This is a test", test_str);
}

void test_strccat_successful(void)
{
char *parts[7] = {"This", " ", "is", " ", "a", " ", "test"};

char *test_str = strccat(parts, 7);

TEST_ASSERT_EQUAL_STRING("This is a test", test_str);
}

int main(void)
{
UNITY_BEGIN();

RUN_TEST(test_cpy_str_to_arr);
RUN_TEST(test_strccat_no_args);
RUN_TEST(test_strccat_incomplete_args);
RUN_TEST(test_strccat_successful);

return UNITY_END();
}

0 comments on commit 485788f

Please sign in to comment.