* svn-archive.c: Initial work on this (not nearly complete yet)
[python/fast-export.git] / svn-archive.c
1 /*
2  * svn-archive.c
3  * ----------
4  *  Walk through a given revision of a local Subversion repository and export 
5  *  all of the contents as a tarfile.
6  *
7  * Author: Chris Lee <clee@kde.org>
8  * License: MIT <http://www.opensource.org/licenses/mit-license.php>
9  */
10
11 #define _XOPEN_SOURCE
12 #include <unistd.h>
13 #include <string.h>
14 #include <stdio.h>
15 #include <time.h>
16
17 #ifndef PATH_MAX
18 #define PATH_MAX 4096
19 #endif
20
21 #include <apr_general.h>
22 #include <apr_lib.h>
23 #include <apr_getopt.h>
24
25 #include <svn_types.h>
26 #include <svn_pools.h>
27 #include <svn_repos.h>
28
29 #undef SVN_ERR
30 #define SVN_ERR(expr) SVN_INT_ERR(expr)
31 #define apr_sane_push(arr, contents) *(char **)apr_array_push(arr) = contents
32
33 #define TRUNK "/trunk"
34
35 time_t get_epoch(char *svn_date)
36 {
37     struct tm tm = {0};
38     char *date = malloc(strlen(svn_date) * sizeof(char *));
39     strncpy(date, svn_date, strlen(svn_date) - 8);
40     strptime(date, "%Y-%m-%dT%H:%M:%S", &tm);
41     free(date);
42     return mktime(&tm);
43 }
44
45 int tar_header(char *path, char *node, size_t size, svn_boolean_t is_dir)
46 {
47     char buf[512];
48     memset(buf, '\0', sizeof(buf));
49     strncpy(buf, node, strlen(node));
50     strncpy(buf+257, "ustar  ", 6);
51     strncpy(buf+265, "clee", 4);
52     strncpy(buf+297, "clee", 4);
53     strncpy(buf+156, (is_dir ? "5" : "0"), 1);
54     strncpy(buf+345, path, strlen(path));
55
56     fwrite(buf, sizeof(char), sizeof(buf), stdout);
57     return 0;
58 }
59
60 int dump_blob(svn_fs_root_t *root, char *prefix, char *path, char *node, apr_pool_t *pool)
61 {
62     char           *full_path, buf[512];
63     apr_size_t     len;
64     svn_stream_t   *stream;
65     svn_filesize_t stream_length;
66
67     full_path = apr_psprintf(pool, "%s%s/%s", prefix, path, node);
68
69     SVN_ERR(svn_fs_file_length(&stream_length, root, full_path, pool));
70     SVN_ERR(svn_fs_file_contents(&stream, root, full_path, pool));
71
72     tar_header(path, node, stream_length, 0);
73
74     do {
75         len = sizeof(buf);
76         memset(buf, '\0', sizeof(buf));
77         SVN_ERR(svn_stream_read(stream, buf, &len));
78         fwrite(buf, sizeof(char), sizeof(buf), stdout);
79         fprintf(stderr, "len %d\n", len);
80     } while (len == sizeof(buf));
81
82     return 0;
83 }
84
85 int dump_tree(svn_fs_root_t *root, char *prefix, char *path, apr_pool_t *pool)
86 {
87     const void       *key;
88     void             *val;
89     char             *node, *subpath, *full_path;
90
91     apr_pool_t       *subpool;
92     apr_hash_t       *dir_entries;
93     apr_hash_index_t *i;
94
95     svn_boolean_t    is_dir;
96
97     tar_header("", path, -1, 1);
98
99     SVN_ERR(svn_fs_dir_entries(&dir_entries, root, apr_psprintf(pool, "%s/%s", prefix, path), pool));
100
101     subpool = svn_pool_create(pool);
102
103     for (i = apr_hash_first(pool, dir_entries); i; i = apr_hash_next(i)) {
104         svn_pool_clear(subpool);
105         apr_hash_this(i, &key, NULL, &val);
106         node = (char *)key;
107
108         subpath = apr_psprintf(subpool, "%s/%s", path, node);
109         full_path = apr_psprintf(subpool, "%s%s", prefix, subpath);
110
111         svn_fs_is_dir(&is_dir, root, full_path, subpool);
112
113         fprintf(stderr, "path: %s\n", full_path);
114         if (is_dir) {
115             dump_tree(root, prefix, subpath, subpool);
116         } else {
117             dump_blob(root, prefix, path, node, subpool);
118         }
119     }
120
121     svn_pool_destroy(subpool);
122
123     return 0;
124 }
125
126 int crawl_revisions(char *repos_path, char *root_path)
127 {
128     const void           *key;
129     void                 *val;
130     char                 *path, *file_change;
131  
132     apr_pool_t           *pool;
133     apr_hash_index_t     *i;
134     apr_hash_t           *props;
135
136     svn_fs_t             *fs;
137     svn_repos_t          *repos;
138     svn_string_t         *author, *committer, *svndate, *svnlog;
139     svn_revnum_t         youngest_rev, export_rev;
140     svn_boolean_t        is_dir;
141     svn_fs_root_t        *root_obj;
142     svn_fs_path_change_t *change;
143
144     pool = svn_pool_create(NULL);
145
146     SVN_ERR(svn_repos_open(&repos, repos_path, pool));
147
148     fs = svn_repos_fs(repos);
149
150     SVN_ERR(svn_fs_initialize(pool));
151     SVN_ERR(svn_fs_youngest_rev(&youngest_rev, fs, pool));
152
153     export_rev = youngest_rev;
154
155     fprintf(stderr, "Exporting archive of r%ld... ", export_rev);
156
157     SVN_ERR(svn_fs_revision_root(&root_obj, fs, export_rev, pool));
158     SVN_ERR(svn_fs_revision_proplist(&props, fs, export_rev, pool));
159
160     svnlog  = apr_hash_get(props, "svn:log", APR_HASH_KEY_STRING);
161     svndate = apr_hash_get(props, "svn:date", APR_HASH_KEY_STRING);
162     author  = apr_hash_get(props, "svn:author", APR_HASH_KEY_STRING);
163
164     dump_tree(root_obj, root_path, "", pool);
165
166     fprintf(stderr, "done!\n");
167
168     return 0;
169 }
170
171 int main(int argc, char *argv[])
172 {
173     apr_getopt_t *os;
174
175     if (argc < 2) {
176         fprintf(stderr, "usage: %s REPOS_PATH [prefix]\n", argv[0]);
177         return -1;
178     }
179
180     if (apr_initialize() != APR_SUCCESS) {
181         fprintf(stderr, "You lose at apr_initialize().\n");
182         return -1;
183     }
184
185     crawl_revisions(argv[1], (argc == 3 ? argv[2] : TRUNK));
186
187     apr_terminate();
188
189     return 0;
190 }