add a 'path' query parameter to maintain path and filename in queries
[webgit] / src / object.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 "object.h"
23 #include "repo.h"
24
25 #define SHA1_HEADER <openssl/sha.h>
26 #include "git/cache.h"
27 #include "git/object.h"
28
29
30 Html
31 webgit_object_link (struct webgit_query* query,
32                    const char* repo,
33                    int repo_len,
34                    const char* object,
35                    int object_len,
36                    const char* path,
37                    Html text)
38 {
39   return html (
40                html_tag ("a",
41                          html_attr ("href", html (
42                                                   html_fmt ("%s?repo=%.*s&object=%.*s",
43                                                             query->request->script_name,
44                                                             repo_len, repo,
45                                                             object_len, object),
46                                                   path ? html_fmt ("&path=%s", path) : html ()
47                                                   )
48                                     )
49                          ),
50                text
51                );
52 }
53
54
55
56 /*
57   Display commits
58 */
59
60 static Html
61 webgit_object_commit_menu_action (struct webgit_query* query, unsigned char* sha1, void* buf, unsigned long size)
62 {
63   return html ("TODO: commit-object sidebar");
64 }
65
66 static Html
67 webgit_object_commit_content_action (struct webgit_query* query, unsigned char* sha1, void* buf, unsigned long size)
68 {
69   Html tree = html_list ();
70   Html parents = html_list ();
71   Html author_text = html_list ();
72   Html author = html_list ();
73   Html committer = html_list ();
74
75   const char* author_beg = NULL;
76   const char* author_end = NULL;
77
78   const char* i = buf;
79   while (i && i < buf+size)
80     {
81       if (*i == '\n')
82         {
83           while (*i == '\n')
84             ++i;
85           break; /* message */
86         }
87       if (!strncmp (i, "tree ", 5))
88         {
89
90           html_list_append (tree,
91                             "Tree: ",
92                             webgit_object_link (query,
93                                                 query->repo, strlen (query->repo),
94                                                 i+5, 40,
95                                                 NULL,
96                                                 html_fmt ("%.40s", i+5))
97                             );
98
99           (i = strchr (i, '\n')) && ++i;
100           continue;
101         }
102       else if (!strncmp (i, "parent ", 7))
103         {
104           html_list_append (parents, html (
105                                            "Parent: ",
106                                            webgit_object_link (query,
107                                                                query->repo, strlen (query->repo),
108                                                                i+7, 40,
109                                                                NULL,
110                                                                html_fmt ("%.40s", i+7))
111                                            )
112                             );
113           (i = strchr (i, '\n')) && ++i;
114           continue;
115         }
116       else if (!strncmp (i, "author ", 7))
117         {
118           char* email_beg = strchr (i, '<');
119           char* email_end = strchr (email_beg, '>');
120
121           author_beg = i+7;
122           author_end = strchr (author_beg, '\n');
123
124           Html name = html_strndup (i+7, email_beg - i - 8);
125           Html email = html_strndup (email_beg + 1, email_end - email_beg - 1);
126
127           struct tm tm;
128           strptime (email_end + 2, "%s %Z", &tm);
129           char pretty_date[256];
130           strftime (pretty_date, 255, "%c", &tm);
131
132           html_list_append (author_text, "Author");
133
134           html_list_append (author, html (
135                                           html ( author_text ), /*BUG: libcwa bug, must be wraped in html()*/
136                                           webgit_email_link (name, email),
137                                           html_strndup (pretty_date, 255)
138                                           )
139                             );
140           (i = strchr (i, '\n')) && ++i;
141           continue;
142         }
143       else if (!strncmp (i, "committer ", 10))
144         {
145           char* email_beg = strchr (i, '<');
146           char* email_end = strchr (email_beg, '>');
147
148           if (author_beg && author_end && strncmp (i + 10, author_beg, author_end - author_beg))
149             {
150               Html name = html_strndup (i+10, email_beg - i - 11);
151               Html email = html_strndup (email_beg + 1, email_end - email_beg - 1);
152
153               struct tm tm;
154               strptime (email_end + 2, "%s %Z", &tm);
155               char pretty_date[256];
156               strftime (pretty_date, 255, "%c", &tm);
157
158               html_list_append (committer, html ("Committer: ",
159                                                  webgit_email_link (name, email),
160                                                  html_strndup (pretty_date, 255)
161                                                  )
162                                 );
163             }
164           else
165             html_list_append (author_text, " and Committer");
166
167           (i = strchr (i, '\n')) && ++i;
168           continue;
169         }
170       ++i;
171     }
172
173   html_list_append (author_text, ": ");
174
175   Html headline = html_list ();
176   Html message = html_list ();
177
178   if (i)
179     {
180       const char* message_beg = strchr (i, '\n');
181
182       if (message_beg)
183         {
184           html_list_append (headline,
185                             html_strndup (i, message_beg - i)
186                             );
187
188           while (*message_beg == '\n')
189             ++message_beg;
190
191           html_list_append (message,
192                             html_strndup (message_beg, SIZE_MAX)
193                             );
194         }
195       else
196         {
197           html_list_append (headline,
198                             html_strndup (i, SIZE_MAX)
199                             );
200           html_list_append (message,
201                             html_strndup (i, SIZE_MAX)
202                             );
203         }
204     }
205
206   /* TODO: diffstat */
207
208
209   return html (
210                html (html_tag ("h3"), headline),
211                html (html_tag ("div"), tree), html_nl (),
212                html (html_tag ("div"), parents), html_nl (),
213                html (html_tag ("div"), author), html_nl (),
214                html (html_tag ("div"), committer), html_nl (),
215                html (html_tag ("pre"), message)
216                );
217 }
218
219 Html
220 webgit_object_commit_action (struct webgit_query* query, unsigned char* sha1)
221 {
222   void* buf;
223   unsigned long size;
224
225   buf = read_object_with_reference (sha1, "commit", &size, NULL);
226
227   return html(
228               html(html_tag("div"), webgit_object_commit_menu_action (query, sha1, buf, size)), html_nl (),
229               html(html_tag("div"), webgit_object_commit_content_action (query, sha1, buf, size)), html_nl ()
230               );
231 }
232
233
234 /*
235   Display trees
236 */
237
238 static Html
239 webgit_object_tree_menu_action (struct webgit_query* query, unsigned char* sha1, struct tree *tree)
240 {
241   return html ("TODO: tree-object sidebar");
242 }
243
244 static const char*
245 pretty_mode (unsigned mode)
246 {
247   if (S_ISGITLINK (mode))
248     return "m---------";
249   else if (S_ISDIR (mode & S_IFMT))
250     return "drwxr-xr-x";
251   else if (S_ISLNK (mode))
252     return "lrwxrwxrwx";
253   else if (S_ISREG (mode))
254     {
255       if (mode & S_IXUSR)
256         return "-rwxr-xr-x";
257       else
258         return "-rw-r--r--";
259     }
260   else
261     return "----------";
262 }
263
264 /* callback has no user-data pointer! */
265 static struct webgit_query* query_in_flight = NULL;
266 static Html tree_in_flight = NULL;
267
268 static int
269 webgit_html_tree (const unsigned char *sha1, const char *base, int baselen,
270                   const char *name, unsigned mode, int stage)
271 {
272   const char pathname[PATH_MAX];
273
274   snprintf (pathname, PATH_MAX-1, "%.*s%s%s", baselen, base, baselen ? "/": "", name);
275
276   if (S_ISGITLINK(mode))
277     {
278       html_list_append (tree_in_flight, html (
279                                               html (
280                                                     html_tag ("tr"),
281                                                     html (html_tag ("td"), pretty_mode (mode)),
282                                                     html (html_tag ("td"),
283                                                           webgit_repo_link (query_in_flight,
284                                                                             query_in_flight->repo,
285                                                                             strlen (query_in_flight->repo),
286                                                                             name, strlen (pathname),
287                                                                             sha1_to_hex (sha1), 40,
288                                                                             "tree",
289                                                                             html_strndup (pathname, SIZE_MAX))
290                                                           ),
291                                                     html (html_tag ("td"), "history summary")
292                                                     ),
293                                               html_nl ()
294                                               )
295                         );
296     }
297   else if (S_ISDIR(mode))
298     {
299       html_list_append (tree_in_flight, html (
300                                               html (html_tag ("tr"),
301                                                     html (html_tag ("td"), pretty_mode (mode)),
302                                                     html (html_tag ("td"),
303                                                           webgit_object_link (query_in_flight,
304                                                                               query_in_flight->repo,
305                                                                               strlen (query_in_flight->repo),
306                                                                               sha1_to_hex (sha1), 40,
307                                                                               pathname,
308                                                                               html_strndup (name, SIZE_MAX))
309                                                           ),
310                                                   html (html_tag ("td"), "history snap")
311                                                   ),
312                                              html_nl ()
313                                              )
314                         );
315     }
316   else
317     {
318       html_list_append (tree_in_flight, html (
319                                               html (html_tag ("tr"),
320                                                     html (html_tag ("td"), pretty_mode (mode)),
321                                                     html (html_tag ("td"),
322                                                           webgit_object_link (query_in_flight,
323                                                                               query_in_flight->repo,
324                                                                               strlen (query_in_flight->repo),
325                                                                               sha1_to_hex (sha1), 40,
326                                                                               pathname,
327                                                                               html_strndup (name, SIZE_MAX))
328                                                           ),
329                                                     html (html_tag ("td"), "history raw edit")
330                                                     ),
331                                               html_nl()
332                                               )
333                         );
334     }
335
336   return 0;
337 }
338
339
340 static Html
341 webgit_object_tree_content_action (struct webgit_query* query, unsigned char* sha1, struct tree *tree)
342 {
343   query_in_flight = query;
344   tree_in_flight = html_list ();
345
346   read_tree_recursive (tree, query->path, query->path ? strlen (query->path): 0, 0, NULL, webgit_html_tree);
347
348   return html (html_tag ("table"), tree_in_flight);
349 }
350
351 Html
352 webgit_object_tree_action (struct webgit_query* query, unsigned char* sha1)
353 {
354   struct tree *tree;
355
356   tree = parse_tree_indirect (sha1);
357   if (!tree)
358     die("not a tree object");
359
360   return html(
361               html(html_tag("div"), webgit_object_tree_menu_action (query, sha1, tree)), html_nl (),
362               html(html_tag("div"), webgit_object_tree_content_action (query, sha1, tree)), html_nl ()
363               );
364 }
365
366
367 /*
368   Display blobs
369 */
370
371 static Html
372 webgit_object_blob_menu_action (struct webgit_query* query, unsigned char* sha1, void* buf, unsigned long size)
373 {
374   return html ("TODO: blob-object sidebar");
375 }
376
377 static Html
378 webgit_object_blob_content_action (struct webgit_query* query, unsigned char* sha1, void* buf, unsigned long size)
379 {
380   if (!memchr(buf, 0, size>8192 ? 8192 : size))
381     {
382       return html (html_tag ("pre"), html_strndup (buf, size));
383     }
384   else
385     {
386       return html ("binary blob");
387     }
388 }
389
390
391 Html
392 webgit_object_blob_action (struct webgit_query* query, unsigned char* sha1)
393 {
394   void* buf;
395   unsigned long size;
396
397   buf = read_object_with_reference (sha1, "blob", &size, NULL);
398
399   return html(
400               html(html_tag("div"), webgit_object_blob_menu_action (query, sha1, buf, size)), html_nl (),
401               html(html_tag("div"), webgit_object_blob_content_action (query, sha1, buf, size)), html_nl ()
402               );
403 }
404
405
406
407
408 /*pretty printer for sourcecode*/
409 //Html
410 //webgit_object_blob_dispatch (struct webgit_query* query, const char *sha1);