e6955cf0015e703ee44001ddeea65d515cbecef1
[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
26 Html
27 webgit_object_link (struct webgit_query* query,
28                    const char* repo,
29                    int repo_len,
30                    const char* object,
31                    int object_len,
32                    const char* path,
33                    Html text)
34 {
35   return html (
36                html_tag ("a",
37                          html_attr ("href", html (
38                                                   html_fmt ("%s?repo=%.*s&object=%.*s",
39                                                             query->request->script_name,
40                                                             repo_len, repo,
41                                                             object_len, object),
42                                                   path ? html_fmt ("&path=%s", path) : html ()
43                                                   )
44                                     )
45                          ),
46                text
47                );
48 }
49
50
51
52 char*
53 webgit_commit_tree_parse (struct commit* commit)
54 {
55   char* tree = strstr (commit->buffer, "tree ");
56   if (!tree)
57     return NULL;
58   return tree + 5;
59 }
60
61 time_t
62 webgit_commit_author_date_parse (struct commit* commit, struct tm* tm)
63 {
64   struct tm tmp;
65   if (!tm)
66     tm = &tmp;
67
68   char* author = strstr (commit->buffer, "author ");
69   if (!author)
70     return (time_t)-1;
71
72   char* beg = strchr (author, '>');
73   if (!beg)
74     return (time_t)-1;
75
76   if (!strptime (beg + 2, "%s %Z", tm))
77     return (time_t)-1;
78
79   return mktime (tm);
80 }
81
82 Html
83 webgit_commit_author_name_parse (struct commit* commit)
84 {
85   char* author = strstr (commit->buffer, "author ");
86   if (!author)
87     return NULL;
88
89   char* end = strchr (author, '<');
90   if (!end)
91     return NULL;
92
93   return html_fmt ("%.*s", end-author-8, author+7);
94 }
95
96 Html
97 webgit_commit_author_email_parse (struct commit* commit)
98 {
99   char* author = strstr (commit->buffer, "author ");
100   if (!author)
101     return NULL;
102
103   char* beg = strchr (author, '<');
104   if (!beg)
105     return NULL;
106
107   char* end = strchr (beg, '>');
108   if (!end)
109     return NULL;
110
111
112   return html_fmt ("%.*s", end-beg-1, beg+1);
113 }
114
115 time_t
116 webgit_commit_committer_date_parse (struct commit* commit, struct tm* tm)
117 {
118   struct tm tmp;
119   if (!tm)
120     tm = &tmp;
121
122   char* committer = strstr (commit->buffer, "committer ");
123   if (!committer)
124     return (time_t)-1;
125
126   char* beg = strchr (committer, '>');
127   if (!beg)
128     return (time_t)-1;
129
130   if (!strptime (beg + 2, "%s %Z", tm))
131     return (time_t)-1;
132
133   return mktime (tm);
134 }
135
136 Html
137 webgit_commit_committer_name_parse (struct commit* commit)
138 {
139   char* committer = strstr (commit->buffer, "committer ");
140   if (!committer)
141     return NULL;
142
143   char* end = strchr (committer, '<');
144   if (!end)
145     return NULL;
146
147   return html_fmt ("%.*s", end-committer-11, committer+10);
148 }
149
150 Html
151 webgit_commit_committer_email_parse (struct commit* commit)
152 {
153   char* committer = strstr (commit->buffer, "committer ");
154   if (!committer)
155     return NULL;
156
157   char* beg = strchr (committer, '<');
158   if (!beg)
159     return NULL;
160
161   char* end = strchr (beg, '>');
162   if (!end)
163     return NULL;
164
165
166   return html_fmt ("%.*s", end-beg-1, beg+1);
167 }
168
169 Html
170 webgit_header_parse (struct commit* commit)
171 {
172   char* header = strstr (commit->buffer, "\n\n");
173   if (!header)
174     return NULL;
175
176   char* end = strchr (header+2, '\n');
177   if (!end)
178     end = header + strlen (header+2);
179   else
180     --end;
181
182   return html_fmt ("%.*s", end-header, header+2);
183 }
184
185 Html
186 webgit_message_parse (struct commit* commit)
187 {
188   char* header = strstr (commit->buffer, "\n\n");
189   if (!header)
190     return NULL;
191
192   header = strchr (header+2, '\n');
193   if (!header)
194     return html ();
195   else
196     ++header;
197
198   return html_fmt ("%.*s", strlen (header), header);
199 }
200
201
202
203
204 /*
205   Display commits
206 */
207
208 static Html
209 webgit_object_commit_menu_action (struct webgit_query* query, unsigned char* sha1, void* buf, unsigned long size)
210 {
211   return html ("TODO: commit-object sidebar");
212 }
213
214 static Html
215 webgit_object_commit_content_action (struct webgit_query* query, unsigned char* sha1, void* buf, unsigned long size)
216 {
217   Html tree = html_list ();
218   Html parents = html_list ();
219   Html author_text = html_list ();
220   Html author = html_list ();
221   Html committer = html_list ();
222
223   const char* author_beg = NULL;
224   const char* author_end = NULL;
225
226   const char* i = buf;
227   while (i && i < buf+size)
228     {
229       if (*i == '\n')
230         {
231           while (*i == '\n')
232             ++i;
233           break; /* message */
234         }
235       if (!strncmp (i, "tree ", 5))
236         {
237
238           html_list_append (tree,
239                             "Tree: ",
240                             webgit_object_link (query,
241                                                 query->repo, strlen (query->repo),
242                                                 i+5, 40,
243                                                 NULL,
244                                                 html_fmt ("%.40s", i+5))
245                             );
246
247           (i = strchr (i, '\n')) && ++i;
248           continue;
249         }
250       else if (!strncmp (i, "parent ", 7))
251         {
252           html_list_append (parents, html (
253                                            "Parent: ",
254                                            webgit_object_link (query,
255                                                                query->repo, strlen (query->repo),
256                                                                i+7, 40,
257                                                                NULL,
258                                                                html_fmt ("%.40s", i+7))
259                                            )
260                             );
261           (i = strchr (i, '\n')) && ++i;
262           continue;
263         }
264       else if (!strncmp (i, "author ", 7))
265         {
266           char* email_beg = strchr (i, '<');
267           char* email_end = strchr (email_beg, '>');
268
269           author_beg = i+7;
270           author_end = strchr (author_beg, '\n');
271
272           Html name = html_strndup (i+7, email_beg - i - 8);
273           Html email = html_strndup (email_beg + 1, email_end - email_beg - 1);
274
275           struct tm tm;
276           strptime (email_end + 2, "%s %Z", &tm);
277           char pretty_date[256];
278           strftime (pretty_date, 255, "%c", &tm);
279
280           html_list_append (author_text, "Author");
281
282           html_list_append (author, html (
283                                           html ( author_text ), /*BUG: libcwa bug, must be wraped in html()*/
284                                           webgit_email_link (name, email),
285                                           html_strndup (pretty_date, 255)
286                                           )
287                             );
288           (i = strchr (i, '\n')) && ++i;
289           continue;
290         }
291       else if (!strncmp (i, "committer ", 10))
292         {
293           char* email_beg = strchr (i, '<');
294           char* email_end = strchr (email_beg, '>');
295
296           if (author_beg && author_end && strncmp (i + 10, author_beg, author_end - author_beg))
297             {
298               Html name = html_strndup (i+10, email_beg - i - 11);
299               Html email = html_strndup (email_beg + 1, email_end - email_beg - 1);
300
301               struct tm tm;
302               strptime (email_end + 2, "%s %Z", &tm);
303               char pretty_date[256];
304               strftime (pretty_date, 255, "%c", &tm);
305
306               html_list_append (committer, html ("Committer: ",
307                                                  webgit_email_link (name, email),
308                                                  html_strndup (pretty_date, 255)
309                                                  )
310                                 );
311             }
312           else
313             html_list_append (author_text, " and Committer");
314
315           (i = strchr (i, '\n')) && ++i;
316           continue;
317         }
318       ++i;
319     }
320
321   html_list_append (author_text, ": ");
322
323   Html headline = html_list ();
324   Html message = html_list ();
325
326   if (i)
327     {
328       const char* message_beg = strchr (i, '\n');
329
330       if (message_beg)
331         {
332           html_list_append (headline,
333                             html_strndup (i, message_beg - i)
334                             );
335
336           while (*message_beg == '\n')
337             ++message_beg;
338
339           html_list_append (message,
340                             html_strndup (message_beg, SIZE_MAX)
341                             );
342         }
343       else
344         {
345           html_list_append (headline,
346                             html_strndup (i, SIZE_MAX)
347                             );
348           html_list_append (message,
349                             html_strndup (i, SIZE_MAX)
350                             );
351         }
352     }
353
354   /* TODO: diffstat */
355
356
357   return html (
358                html (html_tag ("h3"), headline),
359                html (html_tag ("div"), tree), html_nl (),
360                html (html_tag ("div"), parents), html_nl (),
361                html (html_tag ("div"), author), html_nl (),
362                html (html_tag ("div"), committer), html_nl (),
363                html (html_tag ("pre"), message)
364                );
365 }
366
367 Html
368 webgit_object_commit_action (struct webgit_query* query, unsigned char* sha1)
369 {
370   void* buf;
371   unsigned long size;
372
373   buf = read_object_with_reference (sha1, "commit", &size, NULL);
374
375   return html(
376               html(html_tag("div"), webgit_object_commit_menu_action (query, sha1, buf, size)), html_nl (),
377               html(html_tag("div"), webgit_object_commit_content_action (query, sha1, buf, size)), html_nl ()
378               );
379 }
380
381
382 /*
383   Display trees
384 */
385
386 static Html
387 webgit_object_tree_menu_action (struct webgit_query* query, unsigned char* sha1, struct tree *tree)
388 {
389   return html ("TODO: tree-object sidebar");
390 }
391
392 static const char*
393 pretty_mode (unsigned mode)
394 {
395   if (S_ISGITLINK (mode))
396     return "m---------";
397   else if (S_ISDIR (mode & S_IFMT))
398     return "drwxr-xr-x";
399   else if (S_ISLNK (mode))
400     return "lrwxrwxrwx";
401   else if (S_ISREG (mode))
402     {
403       if (mode & S_IXUSR)
404         return "-rwxr-xr-x";
405       else
406         return "-rw-r--r--";
407     }
408   else
409     return "----------";
410 }
411
412 /* callback has no user-data pointer! */
413 static struct webgit_query* query_in_flight = NULL;
414 static Html tree_in_flight = NULL;
415
416 static int
417 webgit_html_tree (const unsigned char *sha1, const char *base, int baselen,
418                   const char *name, unsigned mode, int stage)
419 {
420   const char pathname[PATH_MAX];
421
422   snprintf (pathname, PATH_MAX-1, "%.*s%s%s", baselen, base, baselen ? "/": "", name);
423
424   if (S_ISGITLINK(mode))
425     {
426       html_list_append (tree_in_flight, html (
427                                               html (
428                                                     html_tag ("tr"),
429                                                     html (html_tag ("td"), pretty_mode (mode)),
430                                                     html (html_tag ("td"),
431                                                           webgit_repo_link (query_in_flight,
432                                                                             query_in_flight->repo,
433                                                                             strlen (query_in_flight->repo),
434                                                                             name, strlen (pathname),
435                                                                             sha1_to_hex (sha1), 40,
436                                                                             "tree",
437                                                                             html_strndup (pathname, SIZE_MAX))
438                                                           ),
439                                                     html (html_tag ("td"), "history summary")
440                                                     ),
441                                               html_nl ()
442                                               )
443                         );
444     }
445   else if (S_ISDIR(mode))
446     {
447       html_list_append (tree_in_flight, html (
448                                               html (html_tag ("tr"),
449                                                     html (html_tag ("td"), pretty_mode (mode)),
450                                                     html (html_tag ("td"),
451                                                           webgit_object_link (query_in_flight,
452                                                                               query_in_flight->repo,
453                                                                               strlen (query_in_flight->repo),
454                                                                               sha1_to_hex (sha1), 40,
455                                                                               pathname,
456                                                                               html_strndup (name, SIZE_MAX))
457                                                           ),
458                                                   html (html_tag ("td"), "history snap")
459                                                   ),
460                                              html_nl ()
461                                              )
462                         );
463     }
464   else
465     {
466       html_list_append (tree_in_flight, html (
467                                               html (html_tag ("tr"),
468                                                     html (html_tag ("td"), pretty_mode (mode)),
469                                                     html (html_tag ("td"),
470                                                           webgit_object_link (query_in_flight,
471                                                                               query_in_flight->repo,
472                                                                               strlen (query_in_flight->repo),
473                                                                               sha1_to_hex (sha1), 40,
474                                                                               pathname,
475                                                                               html_strndup (name, SIZE_MAX))
476                                                           ),
477                                                     html (html_tag ("td"), "history raw edit")
478                                                     ),
479                                               html_nl()
480                                               )
481                         );
482     }
483
484   return 0;
485 }
486
487
488 static Html
489 webgit_object_tree_content_action (struct webgit_query* query, unsigned char* sha1, struct tree *tree)
490 {
491   query_in_flight = query;
492   tree_in_flight = html_list ();
493
494   read_tree_recursive (tree, query->path, query->path ? strlen (query->path): 0, 0, NULL, webgit_html_tree);
495
496   return html (html_tag ("table"), tree_in_flight);
497 }
498
499 Html
500 webgit_object_tree_action (struct webgit_query* query, unsigned char* sha1)
501 {
502   struct tree *tree;
503
504   tree = parse_tree_indirect (sha1);
505   if (!tree)
506     die("not a tree object");
507
508   return html(
509               html(html_tag("div"), webgit_object_tree_menu_action (query, sha1, tree)), html_nl (),
510               html(html_tag("div"), webgit_object_tree_content_action (query, sha1, tree)), html_nl ()
511               );
512 }
513
514
515 /*
516   Display blobs
517 */
518
519 static Html
520 webgit_object_blob_menu_action (struct webgit_query* query, unsigned char* sha1, void* buf, unsigned long size)
521 {
522   return html ("TODO: blob-object sidebar");
523 }
524
525 static Html
526 webgit_object_blob_content_action (struct webgit_query* query, unsigned char* sha1, void* buf, unsigned long size)
527 {
528   if (!memchr(buf, 0, size>8192 ? 8192 : size))
529     {
530       return html (html_tag ("pre"), html_strndup (buf, size));
531     }
532   else
533     {
534       const char* mimetype = webgit_mimetype (query->path);
535
536       Html ret;
537
538       if (mimetype && !strncmp(mimetype, "image/", 6))
539         {
540           /* inline image */
541           ret = html (
542                       html_tag ("img",
543                                 html_attr ("src",
544                                            html_fmt ("%s?repo=%s&action=raw&object=%s&path=%s",
545                                                             query->request->script_name,
546                                                             query->repo, query->object, query->path)
547                                            ),
548                                 html_attr ("alt", query->path? query->path : query->object),
549                                 html_attr ("width", "90%")
550                                 )
551                       );
552         }
553       else
554         {
555           /* link to raw data */
556           ret = html ("binary blob");
557         }
558       free (mimetype);
559
560       return ret;
561     }
562 }
563
564
565 Html
566 webgit_object_blob_action (struct webgit_query* query, unsigned char* sha1)
567 {
568   void* buf;
569   unsigned long size;
570
571   buf = read_object_with_reference (sha1, "blob", &size, NULL);
572
573   return html(
574               html(html_tag("div"), webgit_object_blob_menu_action (query, sha1, buf, size)), html_nl (),
575               html(html_tag("div"), webgit_object_blob_content_action (query, sha1, buf, size)), html_nl ()
576               );
577 }
578
579
580
581
582 /*pretty printer for sourcecode*/
583 //Html
584 //webgit_object_blob_dispatch (struct webgit_query* query, const char *sha1);