silence a lot of compiler warnings with some stubs
[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   (void) query;
212   (void) sha1;
213   (void) buf;
214   (void) size;
215
216   return html ("TODO: commit-object sidebar");
217 }
218
219 static Html
220 webgit_object_commit_content_action (struct webgit_query* query, unsigned char* sha1, void* buf, unsigned long size)
221 {
222 /*
223   TODO pass commit objects instead buf/size, use parsers from above
224 */
225   (void) sha1;
226
227   Html tree = html_list ();
228   Html parents = html_list ();
229   Html author_text = html_list ();
230   Html author = html_list ();
231   Html committer = html_list ();
232
233   const char* author_beg = NULL;
234   const char* author_end = NULL;
235
236   const char* i = buf;
237   while (i && (void*)i < buf+size)
238     {
239       if (*i == '\n')
240         {
241           while (*i == '\n')
242             ++i;
243           break; /* message */
244         }
245       if (!strncmp (i, "tree ", 5))
246         {
247           html_list_append (tree,
248                             "Tree: ",
249                             webgit_object_link (query,
250                                                 query->repo, strlen (query->repo),
251                                                 i+5, 40,
252                                                 NULL,
253                                                 html_fmt ("%.40s", i+5))
254                             );
255           if ((i = strchr (i, '\n')))
256             ++i;
257           continue;
258         }
259       else if (!strncmp (i, "parent ", 7))
260         {
261           html_list_append (parents, html (
262                                            "Parent: ",
263                                            webgit_object_link (query,
264                                                                query->repo, strlen (query->repo),
265                                                                i+7, 40,
266                                                                NULL,
267                                                                html_fmt ("%.40s", i+7))
268                                            )
269                             );
270           if ((i = strchr (i, '\n')))
271             ++i;
272           continue;
273         }
274       else if (!strncmp (i, "author ", 7))
275         {
276           char* email_beg = strchr (i, '<');
277           char* email_end = strchr (email_beg, '>');
278
279           author_beg = i+7;
280           author_end = strchr (author_beg, '\n');
281
282           Html name = html_strndup (i+7, email_beg - i - 8);
283           Html email = html_strndup (email_beg + 1, email_end - email_beg - 1);
284
285           struct tm tm;
286           strptime (email_end + 2, "%s %Z", &tm);
287           char pretty_date[256];
288           strftime (pretty_date, 255, "%c", &tm);
289
290           html_list_append (author_text, "Author");
291
292           html_list_append (author, html (
293                                           html ( author_text ), /*BUG: libcwa bug, must be wraped in html()*/
294                                           webgit_email_link (name, email),
295                                           html_strndup (pretty_date, 255)
296                                           )
297                             );
298           if ((i = strchr (i, '\n')))
299             ++i;
300           continue;
301         }
302       else if (!strncmp (i, "committer ", 10))
303         {
304           char* email_beg = strchr (i, '<');
305           char* email_end = strchr (email_beg, '>');
306
307           if (author_beg && author_end && strncmp (i + 10, author_beg, author_end - author_beg))
308             {
309               Html name = html_strndup (i+10, email_beg - i - 11);
310               Html email = html_strndup (email_beg + 1, email_end - email_beg - 1);
311
312               struct tm tm;
313               strptime (email_end + 2, "%s %Z", &tm);
314               char pretty_date[256];
315               strftime (pretty_date, 255, "%c", &tm);
316
317               html_list_append (committer, html ("Committer: ",
318                                                  webgit_email_link (name, email),
319                                                  html_strndup (pretty_date, 255)
320                                                  )
321                                 );
322             }
323           else
324             html_list_append (author_text, " and Committer");
325
326           if ((i = strchr (i, '\n')))
327             ++i;
328           continue;
329         }
330       ++i;
331     }
332
333   html_list_append (author_text, ": ");
334
335   Html headline = html_list ();
336   Html message = html_list ();
337
338   if (i)
339     {
340       const char* message_beg = strchr (i, '\n');
341
342       if (message_beg)
343         {
344           html_list_append (headline,
345                             html_strndup (i, message_beg - i)
346                             );
347
348           while (*message_beg == '\n')
349             ++message_beg;
350
351           html_list_append (message,
352                             html_strndup (message_beg, SIZE_MAX)
353                             );
354         }
355       else
356         {
357           html_list_append (headline,
358                             html_strndup (i, SIZE_MAX)
359                             );
360           html_list_append (message,
361                             html_strndup (i, SIZE_MAX)
362                             );
363         }
364     }
365
366   /* TODO: diffstat */
367
368
369   return html (
370                html (html_tag ("h3"), headline),
371                html (html_tag ("div"), tree), html_nl (),
372                html (html_tag ("div"), parents), html_nl (),
373                html (html_tag ("div"), author), html_nl (),
374                html (html_tag ("div"), committer), html_nl (),
375                html (html_tag ("pre"), message)
376                );
377 }
378
379 Html
380 webgit_object_commit_action (struct webgit_query* query, unsigned char* sha1)
381 {
382   void* buf;
383   unsigned long size;
384
385   buf = read_object_with_reference (sha1, "commit", &size, NULL);
386
387   return html(
388               html(html_tag("div"), webgit_object_commit_menu_action (query, sha1, buf, size)), html_nl (),
389               html(html_tag("div"), webgit_object_commit_content_action (query, sha1, buf, size)), html_nl ()
390               );
391 }
392
393
394 /*
395   Display trees
396 */
397
398 static Html
399 webgit_object_tree_menu_action (struct webgit_query* query, unsigned char* sha1, struct tree *tree)
400 {
401   (void) query;
402   (void) sha1;
403   (void) tree;
404   return html ("TODO: tree-object sidebar");
405 }
406
407 static const char*
408 pretty_mode (unsigned mode)
409 {
410   if (S_ISGITLINK (mode))
411     return "m---------";
412   else if (S_ISDIR (mode & S_IFMT))
413     return "drwxr-xr-x";
414   else if (S_ISLNK (mode))
415     return "lrwxrwxrwx";
416   else if (S_ISREG (mode))
417     {
418       if (mode & S_IXUSR)
419         return "-rwxr-xr-x";
420       else
421         return "-rw-r--r--";
422     }
423   else
424     return "----------";
425 }
426
427 /* callback has no user-data pointer! */
428 static struct webgit_query* query_in_flight = NULL;
429 static Html tree_in_flight = NULL;
430
431 static int
432 webgit_html_tree (const unsigned char *sha1, const char *base, int baselen,
433                   const char *name, unsigned mode, int stage)
434 {
435   (void) stage;
436   char pathname[PATH_MAX];
437
438   snprintf (pathname, PATH_MAX-1, "%.*s%s%s", baselen, base, baselen ? "/": "", name);
439
440   if (S_ISGITLINK(mode))
441     {
442       html_list_append (tree_in_flight, html (
443                                               html (
444                                                     html_tag ("tr"),
445                                                     html (html_tag ("td"), pretty_mode (mode)),
446                                                     html (html_tag ("td"),
447                                                           webgit_repo_link (query_in_flight,
448                                                                             query_in_flight->repo,
449                                                                             strlen (query_in_flight->repo),
450                                                                             name, strlen (pathname),
451                                                                             sha1_to_hex (sha1), 40,
452                                                                             "tree",
453                                                                             html_strndup (pathname, SIZE_MAX))
454                                                           ),
455                                                     html (html_tag ("td"), "history summary")
456                                                     ),
457                                               html_nl ()
458                                               )
459                         );
460     }
461   else if (S_ISDIR(mode))
462     {
463       html_list_append (tree_in_flight, html (
464                                               html (html_tag ("tr"),
465                                                     html (html_tag ("td"), pretty_mode (mode)),
466                                                     html (html_tag ("td"),
467                                                           webgit_object_link (query_in_flight,
468                                                                               query_in_flight->repo,
469                                                                               strlen (query_in_flight->repo),
470                                                                               sha1_to_hex (sha1), 40,
471                                                                               pathname,
472                                                                               html_strndup (name, SIZE_MAX))
473                                                           ),
474                                                   html (html_tag ("td"), "history snap")
475                                                   ),
476                                              html_nl ()
477                                              )
478                         );
479     }
480   else
481     {
482       html_list_append (tree_in_flight, html (
483                                               html (html_tag ("tr"),
484                                                     html (html_tag ("td"), pretty_mode (mode)),
485                                                     html (html_tag ("td"),
486                                                           webgit_object_link (query_in_flight,
487                                                                               query_in_flight->repo,
488                                                                               strlen (query_in_flight->repo),
489                                                                               sha1_to_hex (sha1), 40,
490                                                                               pathname,
491                                                                               html_strndup (name, SIZE_MAX))
492                                                           ),
493                                                     html (html_tag ("td"), "history raw edit")
494                                                     ),
495                                               html_nl()
496                                               )
497                         );
498     }
499
500   return 0;
501 }
502
503
504 static Html
505 webgit_object_tree_content_action (struct webgit_query* query, unsigned char* sha1, struct tree *tree)
506 {
507   (void) sha1;
508   query_in_flight = query;
509   tree_in_flight = html_list ();
510
511   read_tree_recursive (tree, query->path, query->path ? strlen (query->path): 0, 0, NULL, webgit_html_tree);
512
513   return html (html_tag ("table"), tree_in_flight);
514 }
515
516 Html
517 webgit_object_tree_action (struct webgit_query* query, unsigned char* sha1)
518 {
519   struct tree *tree;
520
521   tree = parse_tree_indirect (sha1);
522   if (!tree)
523     die("not a tree object");
524
525   return html(
526               html(html_tag("div"), webgit_object_tree_menu_action (query, sha1, tree)), html_nl (),
527               html(html_tag("div"), webgit_object_tree_content_action (query, sha1, tree)), html_nl ()
528               );
529 }
530
531
532 /*
533   Display blobs
534 */
535
536 static Html
537 webgit_object_blob_menu_action (struct webgit_query* query, unsigned char* sha1, void* buf, unsigned long size)
538 {
539   (void) query;
540   (void) sha1;
541   (void) buf;
542   (void) size;
543   return html ("TODO: blob-object sidebar");
544 }
545
546 static Html
547 webgit_object_blob_content_action (struct webgit_query* query, unsigned char* sha1, void* buf, unsigned long size)
548 {
549   (void) sha1;
550
551   if (!memchr(buf, 0, size>8192 ? 8192 : size))
552     {
553       return html (html_tag ("pre"), html_strndup (buf, size));
554     }
555   else
556     {
557       const char* mimetype = webgit_mimetype (query->path);
558
559       Html ret;
560
561       if (mimetype && !strncmp(mimetype, "image/", 6))
562         {
563           /* inline image */
564           ret = html (
565                       html_tag ("img",
566                                 html_attr ("src",
567                                            html_fmt ("%s?repo=%s&action=raw&object=%s&path=%s",
568                                                             query->request->script_name,
569                                                             query->repo, query->object, query->path)
570                                            ),
571                                 html_attr ("alt", query->path? query->path : query->object),
572                                 html_attr ("width", "90%")
573                                 )
574                       );
575         }
576       else
577         {
578           /* link to raw data */
579           ret = html ("binary blob");
580         }
581       free ((char*)mimetype);
582
583       return ret;
584     }
585 }
586
587
588 Html
589 webgit_object_blob_action (struct webgit_query* query, unsigned char* sha1)
590 {
591   void* buf;
592   unsigned long size;
593
594   buf = read_object_with_reference (sha1, "blob", &size, NULL);
595
596   return html(
597               html(html_tag("div"), webgit_object_blob_menu_action (query, sha1, buf, size)), html_nl (),
598               html(html_tag("div"), webgit_object_blob_content_action (query, sha1, buf, size)), html_nl ()
599               );
600 }
601
602
603
604
605 /*pretty printer for sourcecode*/
606 //Html
607 //webgit_object_blob_dispatch (struct webgit_query* query, const char *sha1);