chris@1544
|
1 /*
|
chris@1544
|
2 * svn-archive.c
|
chris@1544
|
3 * ----------
|
chris@1544
|
4 * Walk through a given revision of a local Subversion repository and export
|
chris@1544
|
5 * all of the contents as a tarfile.
|
chris@1544
|
6 *
|
chris@1544
|
7 * Author: Chris Lee <clee@kde.org>
|
chris@1544
|
8 * License: MIT <http://www.opensource.org/licenses/mit-license.php>
|
chris@1544
|
9 */
|
chris@1544
|
10
|
chris@1544
|
11 #define _XOPEN_SOURCE
|
chris@1544
|
12 #include <unistd.h>
|
chris@1544
|
13 #include <string.h>
|
chris@1544
|
14 #include <stdio.h>
|
chris@1544
|
15 #include <time.h>
|
chris@1544
|
16
|
chris@1544
|
17 #ifndef PATH_MAX
|
chris@1544
|
18 #define PATH_MAX 4096
|
chris@1544
|
19 #endif
|
chris@1544
|
20
|
chris@1544
|
21 #include <apr_general.h>
|
chris@1544
|
22 #include <apr_strings.h>
|
chris@1544
|
23 #include <apr_getopt.h>
|
chris@1544
|
24 #include <apr_lib.h>
|
chris@1544
|
25
|
chris@1544
|
26 #include <svn_types.h>
|
chris@1544
|
27 #include <svn_pools.h>
|
chris@1544
|
28 #include <svn_repos.h>
|
chris@1544
|
29 #include <svn_fs.h>
|
chris@1544
|
30
|
chris@1544
|
31 #undef SVN_ERR
|
chris@1544
|
32 #define SVN_ERR(expr) SVN_INT_ERR(expr)
|
chris@1544
|
33 #define apr_sane_push(arr, contents) *(char **)apr_array_push(arr) = contents
|
chris@1544
|
34
|
chris@1544
|
35 #define TRUNK "/trunk"
|
chris@1544
|
36
|
chris@1544
|
37 static time_t archive_time;
|
chris@1544
|
38
|
chris@1544
|
39 time_t get_epoch(char *svn_date)
|
chris@1544
|
40 {
|
chris@1544
|
41 struct tm tm = {0};
|
chris@1544
|
42 char *date = malloc(strlen(svn_date) * sizeof(char *));
|
chris@1544
|
43 strncpy(date, svn_date, strlen(svn_date) - 8);
|
chris@1544
|
44 strptime(date, "%Y-%m-%dT%H:%M:%S", &tm);
|
chris@1544
|
45 free(date);
|
chris@1544
|
46 return mktime(&tm);
|
chris@1544
|
47 }
|
chris@1544
|
48
|
chris@1544
|
49 int tar_header(apr_pool_t *pool, char *path, char *node, size_t f_size)
|
chris@1544
|
50 {
|
chris@1544
|
51 char buf[512];
|
chris@1544
|
52 unsigned int i, checksum;
|
chris@1544
|
53 svn_boolean_t is_dir;
|
chris@1544
|
54
|
chris@1544
|
55 memset(buf, 0, sizeof(buf));
|
chris@1544
|
56
|
chris@1544
|
57 if ((strlen(path) == 0) && (strlen(node) == 0)) {
|
chris@1544
|
58 return 0;
|
chris@1544
|
59 }
|
chris@1544
|
60
|
chris@1544
|
61 if (strlen(node) == 0) {
|
chris@1544
|
62 is_dir = 1;
|
chris@1544
|
63 } else {
|
chris@1544
|
64 is_dir = 0;
|
chris@1544
|
65 }
|
chris@1544
|
66
|
chris@1544
|
67 if (strlen(path) == 0) {
|
chris@1544
|
68 strncpy(buf, apr_psprintf(pool, "%s", node), 99);
|
chris@1544
|
69 } else if (strlen(path) + strlen(node) < 100) {
|
chris@1544
|
70 strncpy(buf, apr_psprintf(pool, "%s/%s", path+1, node), 99);
|
chris@1544
|
71 } else {
|
chris@1544
|
72 fprintf(stderr, "really long file path...\n");
|
chris@1544
|
73 strncpy(&buf[0], node, 99);
|
chris@1544
|
74 strncpy(&buf[345], path+1, 154);
|
chris@1544
|
75 }
|
chris@1544
|
76
|
chris@1544
|
77 strncpy(&buf[100], apr_psprintf(pool, "%07o", (is_dir ? 0755 : 0644)), 7);
|
chris@1544
|
78 strncpy(&buf[108], apr_psprintf(pool, "%07o", 1000), 7);
|
chris@1544
|
79 strncpy(&buf[116], apr_psprintf(pool, "%07o", 1000), 7);
|
chris@1544
|
80 strncpy(&buf[124], apr_psprintf(pool, "%011lo", f_size), 11);
|
chris@1544
|
81 strncpy(&buf[136], apr_psprintf(pool, "%011lo", archive_time), 11);
|
chris@1544
|
82 strncpy(&buf[156], (is_dir ? "5" : "0"), 1);
|
chris@1544
|
83 strncpy(&buf[257], "ustar ", 8);
|
chris@1544
|
84 strncpy(&buf[265], "clee", 31);
|
chris@1544
|
85 strncpy(&buf[297], "clee", 31);
|
chris@1544
|
86 // strncpy(&buf[329], apr_psprintf(pool, "%07o", 0), 7);
|
chris@1544
|
87 // strncpy(&buf[337], apr_psprintf(pool, "%07o", 0), 7);
|
chris@1544
|
88
|
chris@1544
|
89 strncpy(&buf[148], " ", 8);
|
chris@1544
|
90 checksum = 0;
|
chris@1544
|
91 for (i = 0; i < sizeof(buf); i++) {
|
chris@1544
|
92 checksum += buf[i];
|
chris@1544
|
93 }
|
chris@1544
|
94 strncpy(&buf[148], apr_psprintf(pool, "%07o", checksum & 0x1fffff), 7);
|
chris@1544
|
95
|
chris@1544
|
96 fwrite(buf, sizeof(char), sizeof(buf), stdout);
|
chris@1544
|
97
|
chris@1544
|
98 return 0;
|
chris@1544
|
99 }
|
chris@1544
|
100
|
chris@1544
|
101 int tar_footer()
|
chris@1544
|
102 {
|
chris@1544
|
103 char block[1024];
|
chris@1544
|
104 memset(block, 0, sizeof(block));
|
chris@1544
|
105 fwrite(block, sizeof(char), sizeof(block), stdout);
|
chris@1544
|
106 }
|
chris@1544
|
107
|
chris@1544
|
108 int dump_blob(svn_fs_root_t *root, char *prefix, char *path, char *node, apr_pool_t *pool)
|
chris@1544
|
109 {
|
chris@1544
|
110 char *full_path, buf[512];
|
chris@1544
|
111 apr_size_t len;
|
chris@1544
|
112 svn_stream_t *stream;
|
chris@1544
|
113 svn_filesize_t stream_length;
|
chris@1544
|
114
|
chris@1544
|
115 full_path = apr_psprintf(pool, "%s%s/%s", prefix, path, node);
|
chris@1544
|
116
|
chris@1544
|
117 SVN_ERR(svn_fs_file_length(&stream_length, root, full_path, pool));
|
chris@1544
|
118 SVN_ERR(svn_fs_file_contents(&stream, root, full_path, pool));
|
chris@1544
|
119
|
chris@1544
|
120 tar_header(pool, path, node, stream_length);
|
chris@1544
|
121
|
chris@1544
|
122 do {
|
chris@1544
|
123 len = sizeof(buf);
|
chris@1544
|
124 memset(buf, '\0', sizeof(buf));
|
chris@1544
|
125 SVN_ERR(svn_stream_read(stream, buf, &len));
|
chris@1544
|
126 fwrite(buf, sizeof(char), sizeof(buf), stdout);
|
chris@1544
|
127 } while (len == sizeof(buf));
|
chris@1544
|
128
|
chris@1544
|
129 return 0;
|
chris@1544
|
130 }
|
chris@1544
|
131
|
chris@1544
|
132 int dump_tree(svn_fs_root_t *root, char *prefix, char *path, apr_pool_t *pool)
|
chris@1544
|
133 {
|
chris@1544
|
134 const void *key;
|
chris@1544
|
135 void *val;
|
chris@1544
|
136 char *node, *subpath, *full_path;
|
chris@1544
|
137
|
chris@1544
|
138 apr_pool_t *subpool;
|
chris@1544
|
139 apr_hash_t *dir_entries;
|
chris@1544
|
140 apr_hash_index_t *i;
|
chris@1544
|
141
|
chris@1544
|
142 svn_boolean_t is_dir;
|
chris@1544
|
143
|
chris@1544
|
144 tar_header(pool, path, "", 0);
|
chris@1544
|
145
|
chris@1544
|
146 SVN_ERR(svn_fs_dir_entries(&dir_entries, root, apr_psprintf(pool, "%s/%s", prefix, path), pool));
|
chris@1544
|
147
|
chris@1544
|
148 subpool = svn_pool_create(pool);
|
chris@1544
|
149
|
chris@1544
|
150 for (i = apr_hash_first(pool, dir_entries); i; i = apr_hash_next(i)) {
|
chris@1544
|
151 svn_pool_clear(subpool);
|
chris@1544
|
152 apr_hash_this(i, &key, NULL, &val);
|
chris@1544
|
153 node = (char *)key;
|
chris@1544
|
154
|
chris@1544
|
155 subpath = apr_psprintf(subpool, "%s/%s", path, node);
|
chris@1544
|
156 full_path = apr_psprintf(subpool, "%s%s", prefix, subpath);
|
chris@1544
|
157
|
chris@1544
|
158 svn_fs_is_dir(&is_dir, root, full_path, subpool);
|
chris@1544
|
159
|
chris@1544
|
160 if (is_dir) {
|
chris@1544
|
161 dump_tree(root, prefix, subpath, subpool);
|
chris@1544
|
162 } else {
|
chris@1544
|
163 dump_blob(root, prefix, path, node, subpool);
|
chris@1544
|
164 }
|
chris@1544
|
165 }
|
chris@1544
|
166
|
chris@1544
|
167 svn_pool_destroy(subpool);
|
chris@1544
|
168
|
chris@1544
|
169 return 0;
|
chris@1544
|
170 }
|
chris@1544
|
171
|
chris@1544
|
172 int crawl_filesystem(char *repos_path, char *root_path, apr_pool_t *pool)
|
chris@1544
|
173 {
|
chris@1544
|
174 char *path;
|
chris@1544
|
175
|
chris@1544
|
176 apr_hash_t *props;
|
chris@1544
|
177 apr_hash_index_t *i;
|
chris@1544
|
178
|
chris@1544
|
179 svn_repos_t *repos;
|
chris@1544
|
180 svn_fs_t *fs;
|
chris@1544
|
181 svn_string_t *svndate;
|
chris@1544
|
182 svn_revnum_t youngest_rev, export_rev;
|
chris@1544
|
183 svn_fs_root_t *fs_root;
|
chris@1544
|
184
|
chris@1544
|
185 SVN_ERR(svn_fs_initialize(pool));
|
chris@1544
|
186 SVN_ERR(svn_repos_open(&repos, repos_path, pool));
|
chris@1544
|
187 if ((fs = svn_repos_fs(repos)) == NULL)
|
chris@1544
|
188 return -1;
|
chris@1544
|
189 SVN_ERR(svn_fs_youngest_rev(&youngest_rev, fs, pool));
|
chris@1544
|
190
|
chris@1544
|
191 export_rev = youngest_rev;
|
chris@1544
|
192
|
chris@1544
|
193 SVN_ERR(svn_fs_revision_root(&fs_root, fs, export_rev, pool));
|
chris@1544
|
194 SVN_ERR(svn_fs_revision_proplist(&props, fs, export_rev, pool));
|
chris@1544
|
195
|
chris@1544
|
196 svndate = apr_hash_get(props, "svn:date", APR_HASH_KEY_STRING);
|
chris@1544
|
197 archive_time = get_epoch((char *)svndate->data);
|
chris@1544
|
198
|
chris@1544
|
199 fprintf(stderr, "Exporting archive of r%ld... \n", export_rev);
|
chris@1544
|
200
|
chris@1544
|
201 dump_tree(fs_root, root_path, "", pool);
|
chris@1544
|
202
|
chris@1544
|
203 tar_footer();
|
chris@1544
|
204
|
chris@1544
|
205 fprintf(stderr, "done!\n");
|
chris@1544
|
206
|
chris@1544
|
207 return 0;
|
chris@1544
|
208 }
|
chris@1544
|
209
|
chris@1544
|
210 int main(int argc, char *argv[])
|
chris@1544
|
211 {
|
chris@1544
|
212 apr_pool_t *pool;
|
chris@1544
|
213 apr_getopt_t *options;
|
chris@1544
|
214
|
chris@1544
|
215 apr_getopt_option_t long_options[] = {
|
chris@1544
|
216 { "help", 'h', 0 },
|
chris@1544
|
217 { "prefix", 'p', 0 },
|
chris@1544
|
218 { "basename", 'b', 0 },
|
chris@1544
|
219 { "revision", 'r', 0 },
|
chris@1544
|
220 { NULL, 0, 0 }
|
chris@1544
|
221 };
|
chris@1544
|
222
|
chris@1544
|
223 if (argc < 2) {
|
chris@1544
|
224 fprintf(stderr, "usage: %s REPOS_PATH [prefix]\n", argv[0]);
|
chris@1544
|
225 return -1;
|
chris@1544
|
226 }
|
chris@1544
|
227
|
chris@1544
|
228 if (apr_initialize() != APR_SUCCESS) {
|
chris@1544
|
229 fprintf(stderr, "You lose at apr_initialize().\n");
|
chris@1544
|
230 return -1;
|
chris@1544
|
231 }
|
chris@1544
|
232
|
chris@1544
|
233 pool = svn_pool_create(NULL);
|
chris@1544
|
234
|
chris@1544
|
235 crawl_filesystem(argv[1], (argc == 3 ? argv[2] : TRUNK), pool);
|
chris@1544
|
236
|
chris@1544
|
237 apr_terminate();
|
chris@1544
|
238
|
chris@1544
|
239 return 0;
|
chris@1544
|
240 }
|