Fix missing state transitions in parser.
[svn-fe.git] / svndump.c
blob77bd3a7c07132622b539164e1e0b941f3fe8a063
1 /*
2 * Parse and rearrange a svnadmin dump.
3 * Create the dump with:
4 * svnadmin dump --incremental -r<startrev>:<endrev> <repository> >outfile
5 */
7 #define _GNU_SOURCE
8 #include <string.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <time.h>
13 #include "repo_tree.h"
14 #include "fast_export.h"
15 #include "line_buffer.h"
16 #include "obj_pool.h"
17 #include "string_pool.h"
19 #define NODEACT_REPLACE 4
20 #define NODEACT_DELETE 3
21 #define NODEACT_ADD 2
22 #define NODEACT_CHANGE 1
23 #define NODEACT_UNKNOWN 0
25 #define DUMP_CTX 0
26 #define REV_CTX 1
27 #define NODE_CTX 2
29 #define LENGTH_UNKNOWN (~0)
31 #define BLOB_MARK_OFFSET 1000000000
33 /* Create memory pool for log messages */
34 obj_pool_gen(log, char, 4096);
36 static char* log_copy(uint32_t length, char *log)
38 char *buffer;
39 log_free(log_pool.size);
40 buffer = log_pointer(log_alloc(length));
41 strncpy(buffer, log, length);
42 return buffer;
45 static struct {
46 uint32_t action, propLength, textLength, srcRev, srcMode, mark, type;
47 uint32_t src[REPO_MAX_PATH_DEPTH], dst[REPO_MAX_PATH_DEPTH];
48 } node_ctx;
50 static struct {
51 uint32_t revision;
52 time_t timestamp;
53 char *log, *author;
54 } rev_ctx;
56 static struct {
57 char *uuid, *url;
58 } dump_ctx;
60 static void reset_node_ctx(char *fname)
62 node_ctx.type = 0;
63 node_ctx.action = NODEACT_UNKNOWN;
64 node_ctx.propLength = LENGTH_UNKNOWN;
65 node_ctx.textLength = LENGTH_UNKNOWN;
66 node_ctx.src[0] = ~0;
67 node_ctx.srcRev = 0;
68 node_ctx.srcMode = 0;
69 pool_tok_seq(REPO_MAX_PATH_DEPTH, node_ctx.dst, "/", fname);
70 node_ctx.mark = 0;
73 static void reset_rev_ctx(uint32_t revision)
75 rev_ctx.revision = revision;
76 rev_ctx.timestamp = 0;
77 rev_ctx.log = NULL;
78 if (rev_ctx.author)
79 free(rev_ctx.author);
80 rev_ctx.author = NULL;
83 static void reset_dump_ctx(char *url)
85 if (dump_ctx.url)
86 free(dump_ctx.url);
87 dump_ctx.url = url;
88 if (dump_ctx.uuid)
89 free(dump_ctx.uuid);
90 dump_ctx.uuid = NULL;
93 static uint32_t next_blob_mark(void)
95 static uint32_t mark = BLOB_MARK_OFFSET;
96 return mark++;
99 static void read_props(void)
101 struct tm tm;
102 uint32_t len;
103 uint32_t key = ~0;
104 char *val = NULL;
105 char *t;
106 while ((t = buffer_read_line()) && strcmp(t, "PROPS-END")) {
107 if (!strncmp(t, "K ", 2)) {
108 len = atoi(&t[2]);
109 key = pool_intern(buffer_read_string(len));
110 buffer_read_line();
111 } else if (!strncmp(t, "V ", 2)) {
112 len = atoi(&t[2]);
113 val = buffer_read_string(len);
114 if (key == pool_intern("svn:log")) {
115 /* Value length excludes terminating nul. */
116 rev_ctx.log = log_copy(len + 1, val);
117 } else if (key == pool_intern("svn:author")) {
118 if (rev_ctx.author)
119 free(rev_ctx.author);
120 rev_ctx.author = strdup(val);
121 } else if (key == pool_intern("svn:date")) {
122 strptime(val, "%FT%T", &tm);
123 timezone = 0;
124 tm.tm_isdst = 0;
125 rev_ctx.timestamp = mktime(&tm);
126 } else if (key == pool_intern("svn:executable")) {
127 if (node_ctx.type == REPO_MODE_BLB) {
128 node_ctx.type = REPO_MODE_EXE;
130 } else if (key == pool_intern("svn:special")) {
131 if (node_ctx.type == REPO_MODE_BLB) {
132 node_ctx.type = REPO_MODE_LNK;
135 key = ~0;
136 buffer_read_line();
141 static void handle_node(void)
143 if (node_ctx.propLength != LENGTH_UNKNOWN && node_ctx.propLength) {
144 read_props();
147 if (node_ctx.srcRev) {
148 node_ctx.srcMode =
149 repo_copy(node_ctx.srcRev, node_ctx.src, node_ctx.dst);
152 if (node_ctx.textLength != LENGTH_UNKNOWN &&
153 node_ctx.type != REPO_MODE_DIR) {
154 node_ctx.mark = next_blob_mark();
157 if (node_ctx.action == NODEACT_DELETE) {
158 repo_delete(node_ctx.dst);
159 } else if (node_ctx.action == NODEACT_CHANGE ||
160 node_ctx.action == NODEACT_REPLACE) {
161 if (node_ctx.propLength != LENGTH_UNKNOWN &&
162 node_ctx.textLength != LENGTH_UNKNOWN) {
163 repo_modify(node_ctx.dst, node_ctx.type, node_ctx.mark);
164 } else if (node_ctx.textLength != LENGTH_UNKNOWN) {
165 node_ctx.srcMode = repo_replace(node_ctx.dst, node_ctx.mark);
167 } else if (node_ctx.action == NODEACT_ADD) {
168 if (node_ctx.srcRev &&
169 node_ctx.propLength == LENGTH_UNKNOWN &&
170 node_ctx.textLength != LENGTH_UNKNOWN) {
171 node_ctx.srcMode = repo_replace(node_ctx.dst, node_ctx.mark);
172 } else if (node_ctx.type == REPO_MODE_DIR ||
173 node_ctx.textLength != LENGTH_UNKNOWN){
174 repo_add(node_ctx.dst, node_ctx.type, node_ctx.mark);
178 if (node_ctx.propLength == LENGTH_UNKNOWN && node_ctx.srcMode) {
179 node_ctx.type = node_ctx.srcMode;
182 if (node_ctx.mark) {
183 fast_export_blob(node_ctx.type, node_ctx.mark, node_ctx.textLength);
184 } else if (node_ctx.textLength != LENGTH_UNKNOWN) {
185 buffer_skip_bytes(node_ctx.textLength);
189 static void handle_revision(void)
191 repo_commit(rev_ctx.revision, rev_ctx.author, rev_ctx.log, dump_ctx.uuid,
192 dump_ctx.url, rev_ctx.timestamp);
195 static void svndump_read(char *url)
197 char *val;
198 char *t;
199 uint32_t active_ctx = DUMP_CTX;
200 uint32_t len;
202 reset_dump_ctx(url);
203 while ((t = buffer_read_line())) {
204 val = strstr(t, ": ");
205 if (!val) continue;
206 *val++ = '\0';
207 *val++ = '\0';
209 if(!strcmp(t, "UUID")) {
210 dump_ctx.uuid = strdup(val);
211 } else if (!strcmp(t, "Revision-number")) {
212 if (active_ctx == NODE_CTX) handle_node();
213 if (active_ctx != DUMP_CTX) handle_revision();
214 active_ctx = REV_CTX;
215 reset_rev_ctx(atoi(val));
216 } else if (!strcmp(t, "Node-path")) {
217 if (active_ctx == NODE_CTX)
218 handle_node();
219 active_ctx = NODE_CTX;
220 reset_node_ctx(val);
221 } else if (!strcmp(t, "Node-kind")) {
222 if (!strcmp(val, "dir")) {
223 node_ctx.type = REPO_MODE_DIR;
224 } else if (!strcmp(val, "file")) {
225 node_ctx.type = REPO_MODE_BLB;
226 } else {
227 fprintf(stderr, "Unknown node-kind: %s\n", val);
229 } else if (!strcmp(t, "Node-action")) {
230 if (!strcmp(val, "delete")) {
231 node_ctx.action = NODEACT_DELETE;
232 } else if (!strcmp(val, "add")) {
233 node_ctx.action = NODEACT_ADD;
234 } else if (!strcmp(val, "change")) {
235 node_ctx.action = NODEACT_CHANGE;
236 } else if (!strcmp(val, "replace")) {
237 node_ctx.action = NODEACT_REPLACE;
238 } else {
239 fprintf(stderr, "Unknown node-action: %s\n", val);
240 node_ctx.action = NODEACT_UNKNOWN;
242 } else if (!strcmp(t, "Node-copyfrom-path")) {
243 pool_tok_seq(REPO_MAX_PATH_DEPTH, node_ctx.src, "/", val);
244 } else if (!strcmp(t, "Node-copyfrom-rev")) {
245 node_ctx.srcRev = atoi(val);
246 } else if (!strcmp(t, "Text-content-length")) {
247 node_ctx.textLength = atoi(val);
248 } else if (!strcmp(t, "Prop-content-length")) {
249 node_ctx.propLength = atoi(val);
250 } else if (!strcmp(t, "Content-length")) {
251 len = atoi(val);
252 buffer_read_line();
253 if (active_ctx == REV_CTX) {
254 read_props();
255 } else if (active_ctx == NODE_CTX) {
256 handle_node();
257 active_ctx = REV_CTX;
258 } else {
259 fprintf(stderr, "Unexpected content length header: %d\n", len);
260 buffer_skip_bytes(len);
264 if (active_ctx == NODE_CTX) handle_node();
265 if (active_ctx != DUMP_CTX) handle_revision();
268 static void svndump_reset(void)
270 log_reset();
271 buffer_reset();
272 repo_reset();
273 reset_dump_ctx(NULL);
274 reset_rev_ctx(0);
275 reset_node_ctx(NULL);
278 int main(int argc, char **argv)
280 svndump_read((argc > 1) ? argv[1] : NULL);
281 svndump_reset();
282 return 0;