completing the links for the repo listing page
[webgit] / src / repo.c
1 /*
2     cehtehs git web frontend
3
4   Copyright (C)
5     2007,               Christian Thaeter <ct@pipapo.org>
6
7   This program is free software; you can redistribute it and/or
8   modify it under the terms of the GNU General Public License as
9   published by the Free Software Foundation; either version 2 of the
10   License, or (at your option) any later version.
11
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16
17   You should have received a copy of the GNU General Public License
18   along with this program; if not, write to the Free Software
19   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #include "repo.h"
23 #include "age.h"
24
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <unistd.h>
28 #include <pwd.h>
29 #include <stdint.h>
30 #include <time.h>
31
32 static struct ctgit_repo_info* in_flight;       /* stupid git callback has no void* userdata; we have to pass self in a global */
33
34 static void
35 ctgit_name_conf (struct ctgit_repo_info* self, const char *value)
36 {
37   self->name = cwa_strndup (value, SIZE_MAX);
38 }
39
40
41 static void
42 ctgit_description_conf (struct ctgit_repo_info* self, const char *value)
43 {
44   self->description = cwa_strndup (value, SIZE_MAX);
45 }
46
47
48 static void
49 ctgit_giturl_conf (struct ctgit_repo_info* self, const char *value)
50 {
51   self->giturl = cwa_strndup (value, SIZE_MAX);
52 }
53
54
55 static void
56 ctgit_readme_conf (struct ctgit_repo_info* self, const char *value)
57 {
58   self->readme = cwa_strndup (value, SIZE_MAX);
59 }
60
61
62 static void
63 ctgit_owner_conf (struct ctgit_repo_info* self, const char *value)
64 {
65   self->owner = cwa_strndup (value, SIZE_MAX);
66 }
67
68
69 int
70 ctgit_repo_conf_cb (const char *var, const char *value)
71 {
72 #define CTGIT_CONF(name, _) {CTGIT_CONF_PREFIX #name, ctgit_##name##_conf},
73   struct conf_table{
74     char* name;
75     void (*cb)(struct ctgit_repo_info* self, const char *value);
76   } cmds[] = {CTGIT_CONFS {"", NULL}};
77 #undef CTGIT_CONF
78
79   for (struct conf_table* j = cmds; j->cb; ++j)
80     {
81       if (!strcmp (j->name, var))
82         {
83           j->cb (in_flight, value);
84           break;
85         }
86     }
87   return 1;
88 }
89
90 struct ctgit_repo_info*
91 ctgit_repoinfo_new (struct ctgit_query* query, const char* path)
92 {
93   if (!path)
94     return NULL;
95
96   struct ctgit_repo_info* self = cwa_malloc (sizeof(struct ctgit_repo_info));
97
98   self->name = NULL;
99   self->owner = NULL;
100   self->description = NULL;
101   self->giturl = NULL;
102   self->readme = NULL;
103
104   self->last_commit = NULL;
105   self->last_tree = NULL;
106   self->last_head = NULL;
107
108   llist_init (&self->node);
109   self->path = cwa_strndup (path, SIZE_MAX);
110
111   chdir (path);
112
113   const char* subdir = setup_git_directory ();
114   /* strip subdir part off */
115   self->path [strlen (path) - (subdir ? strlen (subdir):0)] = '\0';
116
117   struct stat st;
118
119   if (!stat (".git/refs/heads", &st))
120     self->age = difftime (query->now, st.st_mtime);
121   else
122     self->age = ~0;
123
124   /* find the head which points to the last commit */
125   FILE* branches = ctgit_git_open ("branch --no-color --no-abbrev -v");
126   char name[128] = ".git/refs/heads/";
127   char sha1[41];
128   char* head = name + 16;
129
130   while (fscanf (branches, "%*[* ] %111s %40s", head, sha1) == 2)
131     {
132       while (fgetc(branches) != '\n');
133
134       if (!stat (name, &st))
135         if (difftime (query->now, st.st_mtime) == self->age)
136           break;
137     }
138   ctgit_git_close (branches);
139   self->last_commit = cwa_strndup (sha1, 40);
140   self->last_head = cwa_strndup (name + 5, SIZE_MAX);
141
142   /* find the tree for the last commit */
143   FILE* commit = ctgit_git_open ("cat-file commit %s", sha1);
144   if (fscanf (commit, "tree %40s", sha1) == 1)
145     {
146       self->last_tree = cwa_strndup (sha1, 40);
147     }
148   ctgit_git_close (commit);
149
150   /* get [ctgit] subsection out of .git/config */
151   in_flight = self;
152   git_config (ctgit_repo_conf_cb);
153
154   if (!self->name)
155     {
156       /* strip 'name' out of path when not in .git/config */
157       const char* n;
158       while ((n = strrchr (self->path, '/')))
159         if (n[1])
160           {
161             ++n;
162             break;
163           }
164
165       if (!n)
166         n = self->path;
167
168       self->name = cwa_strndup (n, SIZE_MAX);
169     }
170
171   char buf[512];
172   //struct stat st;
173   if (stat (path, &st))
174     return NULL; // error ILLEGAL dir
175
176   struct passwd* pw;
177   if (!self->owner)
178     {
179       if ((pw = getpwuid (st.st_uid)) != NULL)
180         self->owner = cwa_strndup (pw->pw_gecos, strchr (pw->pw_gecos, ',') - pw->pw_gecos);
181       else
182         self->owner = cwa_strndup ("unknown", 100);
183     }
184
185
186   //snprintf (buf, 512, "%s/.git/description", path);
187
188   //FILE* desc = fopen (buf, "r");
189   if (!self->description)
190     {
191       FILE* desc = fopen (".git/description", "r");
192       if (desc)
193         {
194           fgets (buf, 512, desc);
195           fclose (desc);
196           char* e = strchr (buf, '\n');
197           if (e)
198             *e = '\0';
199
200           self->description = cwa_strndup (buf, 40);
201         }
202     }
203
204   if (self->description)
205     {
206       size_t l = strlen (self->description);
207       if (l >= 39)
208         {
209           self->description[l-1] = '.';
210           self->description[l-2] = '.';
211           self->description[l-3] = '.';
212         }
213     }
214
215
216   return self;
217 }
218
219 void
220 ctgit_repoinfo_free (struct ctgit_repo_info* self)
221 {
222   if (self)
223     {
224       llist_unlink (&self->node);
225
226       free (self->path);
227       free (self->name);
228
229       free (self->owner);
230       free (self->description);
231       free (self->giturl);
232       free (self->readme);
233
234       free (self->last_commit);
235       free (self->last_tree);
236       free (self->last_head);
237
238       free (self);
239     }
240 }