Skip to content

Commit d89c9c3

Browse files
authored
Merge pull request #2 from supechicken/main
Add Ruby support for `crew-mvdir`
2 parents dc6e429 + 6d7de22 commit d89c9c3

7 files changed

Lines changed: 200 additions & 66 deletions

File tree

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,6 @@ cc ./crew-mvdir.c -O2 -o crew-mvdir
2020
```
2121

2222
## License
23-
Copyright (C) 2013-2023 Chromebrew Authors
23+
Copyright (C) 2013-2024 Chromebrew Authors
2424

25-
This project including all of its source files is released under the terms of [GNU General Public License (version 3 or later)](http://www.gnu.org/licenses/gpl.txt).
25+
This project including all of its source files is released under the terms of [GNU General Public License (version 3 or later)](http://www.gnu.org/licenses/gpl.txt).

build.sh

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#!/bin/bash -ex
2+
3+
mkdir -p builddir
4+
cd builddir
5+
6+
# build shared library
7+
cc -shared -fPIC ${CFLAGS} ../src/mvdir.c -o crew-mvdir.so.1
8+
9+
# build CLI for use in install.sh
10+
cc ${CFLAGS} -L . -l:crew-mvdir.so.1 ../src/main.c -o crew-mvdir
11+
12+
# build ruby binding for use in crew
13+
ruby ../src/ruby_binding/extconf.rb
14+
make -j"$(nproc)"
15+
16+
echo 'Build completed.'

src/main.c

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
Copyright (C) 2013-2024 Chromebrew Authors
3+
4+
This program is free software: you can redistribute it and/or modify
5+
it under the terms of the GNU General Public License as published by
6+
the Free Software Foundation, either version 3 of the License, or
7+
(at your option) any later version.
8+
9+
This program is distributed in the hope that it will be useful,
10+
but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
GNU General Public License for more details.
13+
14+
You should have received a copy of the GNU General Public License
15+
along with this program. If not, see https://www.gnu.org/licenses/gpl-3.0.html.
16+
*/
17+
18+
/*
19+
crew-mvdir: Move all files under one directory to another,
20+
override conflict files in the destination directory (merge two directories)
21+
22+
Equivalent of `rsync -ahHAXW --remove-source-files dir1/ dir2/`
23+
24+
crew-mvdir use rename() syscall to move files (instead of copying-deleting), that's why it is faster than `rsync`
25+
26+
Usage: ./crew-mvdir [-v] [-n] [src] [dst]
27+
28+
cc ./crew-mvdir.c -O2 -o crew-mvdir
29+
*/
30+
31+
#include <stdio.h>
32+
#include <stdlib.h>
33+
#include <stdbool.h>
34+
#include <string.h>
35+
#include <unistd.h>
36+
#include "./mvdir.h"
37+
38+
int main(int argc, char** argv) {
39+
struct mvdir_opts *opts = calloc(1, sizeof opts);
40+
int opt;
41+
42+
// initialize mvdir_opts struct
43+
opts->src = calloc(sizeof(char), PATH_MAX);
44+
opts->dst = calloc(sizeof(char), PATH_MAX);
45+
46+
while ((opt = getopt(argc, argv, "vn")) != -1) {
47+
switch (opt) {
48+
case 'v':
49+
// verbose mode
50+
opts->verbose = true;
51+
break;
52+
case 'n':
53+
// do not overwrite an existing file
54+
opts->no_clobber = true;
55+
break;
56+
default:
57+
fprintf(stderr, "Usage: %s [-v] [-n] [src] [dst]\n", argv[0]);
58+
exit(EXIT_FAILURE);
59+
break;
60+
}
61+
}
62+
63+
if (argc - optind != 2) {
64+
fprintf(stderr, "Usage: %s [-v] [-n] [src] [dst]\n", argv[0]);
65+
exit(EXIT_FAILURE);
66+
}
67+
68+
strcpy(opts->src, argv[optind]);
69+
strcpy(opts->dst, argv[optind + 1]);
70+
71+
return move_directory(opts);
72+
}

crew-mvdir.c renamed to src/mvdir.c

Lines changed: 23 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright (C) 2013-2023 Chromebrew Authors
2+
Copyright (C) 2013-2024 Chromebrew Authors
33
44
This program is free software: you can redistribute it and/or modify
55
it under the terms of the GNU General Public License as published by
@@ -15,34 +15,20 @@
1515
along with this program. If not, see https://www.gnu.org/licenses/gpl-3.0.html.
1616
*/
1717

18-
/*
19-
crew-mvdir: Move all files under one directory to another,
20-
override conflict files in the destination directory (merge two directories)
21-
22-
Equivalent of `rsync -ahHAXW --remove-source-files dir1/ dir2/`
23-
24-
crew-mvdir use rename() syscall to move files (instead of copying-deleting), that's why it is faster than `rsync`
25-
26-
Usage: ./crew-mvdir [-v] [-n] [src] [dst]
27-
28-
cc ./crew-mvdir.c -O2 -o crew-mvdir
29-
*/
30-
3118
#define _XOPEN_SOURCE 700 // for nftw()
3219
#include <stdio.h>
3320
#include <stdlib.h>
3421
#include <stdbool.h>
3522
#include <fcntl.h>
3623
#include <unistd.h>
3724
#include <errno.h>
38-
#include <limits.h>
3925
#include <string.h>
4026
#include <ftw.h>
4127
#include <sys/sendfile.h>
4228
#include <sys/stat.h>
29+
#include "./mvdir.h"
4330

44-
bool same_fs = true, verbose = false, no_clobber = false;
45-
char src[PATH_MAX], dst[PATH_MAX];
31+
struct mvdir_opts *opts;
4632

4733
void copy_and_delete_file(const struct stat *src_info, const char *src_path, const char *dst_path) {
4834
// copy_and_delete_file(): copy a file and delete it after copying, used as a fallback when rename() does not work
@@ -77,34 +63,34 @@ void copy_and_delete_file(const struct stat *src_info, const char *src_path, con
7763
int move_file(const char *src_path, const struct stat *src_info, int flag, struct FTW *ftwbuf) {
7864
char dst_path[PATH_MAX];
7965

80-
strcpy(dst_path, dst);
81-
strcat(dst_path, src_path + strlen(src));
66+
strcpy(dst_path, opts->dst);
67+
strcat(dst_path, src_path + strlen(opts->src));
8268

8369
if (strcmp(src_path, ".") == 0) return 0;
84-
if (verbose) fprintf(stderr, "%s -> %s\n", src_path, dst_path);
70+
if (opts->verbose) fprintf(stderr, "%s -> %s\n", src_path, dst_path);
8571

8672
switch (flag) {
8773
case FTW_F:
8874
case FTW_SL:
8975
case FTW_SLN:
9076
if (access(dst_path, F_OK) == 0) {
9177
// do not touch existing files if -n specified
92-
if (!(no_clobber || remove(dst_path) == 0)) {
78+
if (!(opts->no_clobber || remove(dst_path) == 0)) {
9379
// remove file with same name in dest (if exist)
9480
fprintf(stderr, "%s: failed to remove file: %s\n", dst_path, strerror(errno));
9581
exit(errno);
9682
}
9783
}
9884

9985
// move (rename) file to dest
100-
if (same_fs) {
86+
if (opts->same_fs) {
10187
// (when source and destination are in the same filesystem)
10288
// file/symlink: move file to dest, override files with same filename in dest (mode/owner remain unchanged)
10389
if (rename(src_path, dst_path) == -1) {
10490
if (errno == EXDEV) {
10591
// fallback to copying-deleting if source and destination are not in the same filesystem
106-
same_fs = false;
107-
if (verbose) fprintf(stderr, "Warning: destination is not in the same filesystem, will fallback to sendfile() instead.\n");
92+
opts->same_fs = false;
93+
if (opts->verbose) fprintf(stderr, "Warning: destination is not in the same filesystem, will fallback to sendfile() instead.\n");
10894
return move_file(src_path, src_info, flag, ftwbuf);
10995
}
11096

@@ -142,7 +128,7 @@ int move_file(const char *src_path, const struct stat *src_info, int flag, struc
142128
if (access(dst_path, F_OK) == -1) {
143129
mode_t dir_mode = src_info->st_mode;
144130

145-
if (verbose) fprintf(stderr, "Creating directory %s\n", dst_path);
131+
if (opts->verbose) fprintf(stderr, "Creating directory %s\n", dst_path);
146132
if (mkdir(dst_path, dir_mode) == -1) {
147133
fprintf(stderr, "%s: mkdir() failed: %s\n", src_path, strerror(errno));
148134
exit(errno);
@@ -158,50 +144,23 @@ int move_file(const char *src_path, const struct stat *src_info, int flag, struc
158144
return 0;
159145
}
160146

161-
int main(int argc, char** argv) {
162-
int opt;
163-
164-
while ((opt = getopt(argc, argv, "vn")) != -1) {
165-
switch (opt) {
166-
case 'v':
167-
// verbose mode
168-
verbose = true;
169-
break;
170-
case 'n':
171-
// do not overwrite an existing file
172-
no_clobber = true;
173-
break;
174-
default:
175-
fprintf(stderr, "Usage: %s [-v] [-n] [src] [dst]\n", argv[0]);
176-
exit(EXIT_FAILURE);
177-
break;
178-
}
179-
}
180-
181-
if (argc - optind != 2) {
182-
fprintf(stderr, "Usage: %s [-v] [-n] [src] [dst]\n", argv[0]);
183-
exit(EXIT_FAILURE);
184-
}
185-
186-
strcpy(src, argv[optind]);
187-
strcpy(dst, argv[optind + 1]);
188-
189-
struct stat src_info, dst_info;
190-
stat(src, &src_info);
191-
stat(dst, &dst_info);
147+
int move_directory(struct mvdir_opts *optPtr) {
148+
opts = optPtr;
192149

193150
// trailing slashes in path are required in order to make move_file() works
194-
int src_len = strlen(src),
195-
dst_len = strlen(dst);
151+
int src_len = strlen(opts->src),
152+
dst_len = strlen(opts->dst);
196153

197-
if (src[src_len - 1] != '/') src[src_len] = '/';
198-
if (dst[dst_len - 1] != '/') dst[dst_len] = '/';
154+
if (opts->src[src_len - 1] != '/') opts->src[src_len] = '/';
155+
if (opts->dst[dst_len - 1] != '/') opts->dst[dst_len] = '/';
199156

200157
// call move_file() with files in src recursively
201-
if (nftw(src, move_file, 100, FTW_PHYS | FTW_MOUNT) == -1) {
202-
fprintf(stderr, "%s: nftw() failed: %s\n", src, strerror(errno));
158+
int ret = nftw(opts->src, move_file, 100, FTW_PHYS | FTW_MOUNT);
159+
160+
if (ret == -1) {
161+
fprintf(stderr, "%s: nftw() failed: %s\n", opts->src, strerror(errno));
203162
exit(errno);
204163
}
205164

206-
return 0;
207-
}
165+
return ret;
166+
}

src/mvdir.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
Copyright (C) 2013-2024 Chromebrew Authors
3+
4+
This program is free software: you can redistribute it and/or modify
5+
it under the terms of the GNU General Public License as published by
6+
the Free Software Foundation, either version 3 of the License, or
7+
(at your option) any later version.
8+
9+
This program is distributed in the hope that it will be useful,
10+
but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
GNU General Public License for more details.
13+
14+
You should have received a copy of the GNU General Public License
15+
along with this program. If not, see https://www.gnu.org/licenses/gpl-3.0.html.
16+
*/
17+
18+
#include <limits.h>
19+
#include <stdbool.h>
20+
21+
struct mvdir_opts {
22+
char *src, *dst;
23+
bool same_fs, verbose, no_clobber;
24+
};
25+
26+
int move_directory(struct mvdir_opts *optPtr);

src/ruby_binding/crew_mvdir.c

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
Copyright (C) 2013-2024 Chromebrew Authors
3+
4+
This program is free software: you can redistribute it and/or modify
5+
it under the terms of the GNU General Public License as published by
6+
the Free Software Foundation, either version 3 of the License, or
7+
(at your option) any later version.
8+
9+
This program is distributed in the hope that it will be useful,
10+
but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
GNU General Public License for more details.
13+
14+
You should have received a copy of the GNU General Public License
15+
along with this program. If not, see https://www.gnu.org/licenses/gpl-3.0.html.
16+
*/
17+
18+
/*
19+
ruby_binding/crew_mvdir.c: Ruby binding for use in `crew`
20+
21+
Usage: crew_mvdir(src, dst, verbose: false)
22+
*/
23+
24+
#include <string.h>
25+
#include <ruby.h>
26+
#include "../mvdir.h"
27+
28+
VALUE crew_mvdir(int argc, VALUE *argv, VALUE self) {
29+
struct mvdir_opts *opts = calloc(1, sizeof(opts));
30+
VALUE src, dst, rb_opts, verbose;
31+
32+
// initialize mvdir_opts struct
33+
opts->src = calloc(sizeof(char), PATH_MAX);
34+
opts->dst = calloc(sizeof(char), PATH_MAX);
35+
36+
// parse argument passed in
37+
rb_scan_args(argc, argv, "2:", &src, &dst, &rb_opts);
38+
39+
if (NIL_P(rb_opts)) rb_opts = rb_hash_new();
40+
41+
// read extra options
42+
verbose = rb_hash_aref(rb_opts, rb_intern("verbose"));
43+
if (NIL_P(verbose)) verbose = Qfalse;
44+
45+
// convert Ruby values to C style data types
46+
strcpy(opts->src, StringValueCStr(src));
47+
strcpy(opts->dst, StringValueCStr(dst));
48+
opts->verbose = RTEST(verbose);
49+
50+
move_directory(opts);
51+
return Qnil;
52+
}
53+
54+
void Init_crew_mvdir() {
55+
rb_define_global_function("crew_mvdir", crew_mvdir, -1);
56+
}

src/ruby_binding/extconf.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
require 'mkmf'
2+
3+
$LDFLAGS += " -L#{Dir.pwd} -l:crew-mvdir.so.1"
4+
5+
create_makefile 'crew_mvdir'

0 commit comments

Comments
 (0)