initial commit
[reposerve] / server / src / repo.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <git2.h>
5
6 struct repofile {
7         char *path;
8 };
9
10 static int proc_tree(git_repository *repo, git_tree *tree, const char *path);
11
12 static struct repofile *files;
13 static int num_files, max_files;
14
15 int repo_init(const char *path)
16 {
17         int res = -1;
18         git_repository *repo = 0;
19         git_tree *tree;
20
21         git_libgit2_init();
22
23         if(git_repository_open_ext(&repo, path, GIT_REPOSITORY_OPEN_NO_SEARCH, 0) != 0) {
24                 fprintf(stderr, "repo_init: failed to open repository: %s: %s\n", path,
25                                 git_error_last()->message);
26                 goto end;
27         }
28
29         files = 0;
30         num_files = max_files = 0;
31
32         if(git_revparse_single((git_object**)&tree, repo, "HEAD^{tree}") != 0) {
33                 fprintf(stderr, "repo_init: %s\n", git_error_last()->message);
34                 goto end;
35         }
36         if(proc_tree(repo, tree, 0) == -1) {
37                 goto end;
38         }
39
40         res = 0;
41 end:
42         if(res != 0) {
43                 free(files);
44                 files = 0;
45                 num_files = max_files = 0;
46         }
47         if(repo) git_repository_free(repo);
48         git_libgit2_shutdown();
49         return res;
50 }
51
52 void repo_cleanup(void)
53 {
54         free(files);
55         files = 0;
56         num_files = max_files = 0;
57 }
58
59 int repo_num_files(void)
60 {
61         return num_files;
62 }
63
64 const char *repo_file(int idx)
65 {
66         if(idx < 0 || idx >= num_files) return 0;
67         return files[idx].path;
68 }
69
70 static int add_file(const char *name, const char *path)
71 {
72         struct repofile *file;
73         int pathlen;
74
75         if(num_files >= max_files) {
76                 int newmax = max_files ? max_files * 2 : 16;
77                 void *newfiles = realloc(files, newmax * sizeof *files);
78                 if(!newfiles) {
79                         fprintf(stderr, "repo_init: failed to add file\n");
80                         return -1;
81                 }
82                 files = newfiles;
83                 max_files = newmax;
84         }
85
86         pathlen = strlen(name);
87         if(path) {
88                 pathlen += strlen(path) + 1;
89         }
90
91         file = files + num_files;
92         if(!(file->path = malloc(pathlen + 1))) {
93                 fprintf(stderr, "repo_init: failed to allocate file path buffer\n");
94                 return -1;
95         }
96         if(path) {
97                 sprintf(file->path, "%s/%s", path, name);
98         } else {
99                 strcpy(file->path, name);
100         }
101
102         num_files++;
103         return 0;
104 }
105
106 static int proc_tree(git_repository *repo, git_tree *tree, const char *path)
107 {
108         int i, count = git_tree_entrycount(tree);
109         const git_tree_entry *ent;
110         git_object_t type;
111         git_object *obj;
112         const char *name;
113
114         for(i=0; i<count; i++) {
115                 ent = git_tree_entry_byindex(tree, i);
116                 type = git_tree_entry_type(ent);
117                 name = git_tree_entry_name(ent);
118
119                 switch(type) {
120                 case GIT_OBJECT_BLOB:
121                         if(add_file(name, path) == -1) {
122                                 return -1;
123                         }
124                         break;
125
126                 case GIT_OBJECT_TREE:
127                         git_tree_entry_to_object(&obj, repo, ent);
128                         if(path) {
129                                 char *pathbuf = malloc(strlen(path) + strlen(name) + 1);
130                                 if(!pathbuf) {
131                                         fprintf(stderr, "repo_init: failed to allocate memory\n");
132                                         return -1;
133                                 }
134                                 sprintf(pathbuf, "%s/%s", path, name);
135                                 proc_tree(repo, (git_tree*)obj, pathbuf);
136                                 free(pathbuf);
137                         } else {
138                                 proc_tree(repo, (git_tree*)obj, name);
139                         }
140                         git_object_free(obj);
141                         break;
142
143                 default:
144                         break;
145                 }
146         }
147
148         return 0;
149 }