root/git/git.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. cleanup
  2. setup_filters
  3. switch_object
  4. setup_git
  5. page_setup
  6. check_gerr
  7. check_ptr
  8. make_query
  9. make_query_simple
  10. make_snapshot_name
  11. make_tag_summary
  12. commit_to_diff
  13. do_query
  14. env_setup
  15. do_archive
  16. main

   1 #include <stdio.h>
   2 #include <string.h>
   3 #include <time.h>
   4 #include <unistd.h>
   5 #include <sys/wait.h>
   6 #include <sys/stat.h>
   7 #include <git2.h>
   8 
   9 #ifndef MAX_CACHE
  10 # define MAX_CACHE      67108864 /* 64 MiB */
  11 #endif
  12 #ifndef MAX_SINGLE_CACHE
  13 # define MAX_SINGLE_CACHE       1048576 /* 1 MiB */
  14 #endif
  15 #ifndef CACHE_CODE
  16 # define CACHE_CODE     "4D00A18"
  17 #endif
  18 #ifndef TMP_DIR
  19 # define TMP_DIR        "/tmp"
  20 #endif
  21 
  22 #include "html.h"
  23 #include "git.h"
  24 #include "ui-log.h"
  25 #include "ui-commit.h"
  26 #include "ui-tree.h"
  27 #include "ui-refs.h"
  28 
  29 FILE *capture;
  30 int has_page = 0;
  31 int binary_xfer = 0;
  32 int git_init = 0;
  33 char rname[MINB] = "",
  34      title[MINB] = "";
  35 
  36 struct q query = {0};
  37 
  38 git_repository *grepo = NULL;
  39 const git_oid *gbase = NULL;
  40 
  41 struct cache cstate = {0};
  42 
  43 
  44 void
  45 cleanup(int e)
  46 {
  47         if (!binary_xfer) {
  48                 html("<div class=\"git-ag-footer\">\n\
  49   <hr>\n\
  50   <p>Generated %s%s</p>\n\
  51 </div>\n\
  52 </div>\n",
  53                     check_sptr(readable_time_static(time(NULL),
  54     MY_LONG_DATE_FMT)),
  55                     query.can_compress ? " (gzip)" : "");
  56                 html_page_end();
  57         }
  58 
  59         if (git_init)
  60                 git_libgit2_shutdown();
  61 
  62         fclose(stdout);
  63         cache_end(e);
  64 
  65         exit(e);
  66 }
  67 
  68 
  69 static void
  70 setup_filters(void)
  71 {
  72         if (!T_IS(T_ALREADY_COMPRESSED))
  73                 query.can_compress = gzip_init() == 0;
  74 
  75         if (cache_init(&query, sizeof(struct q)) == 1) {
  76                 /* we are replaying a cache entry */
  77                 binary_xfer = 1;
  78 
  79                 cleanup(0);
  80         }
  81 }
  82 
  83 
  84 static void
  85 switch_object(const char *name)
  86 {
  87         git_object *tobject;
  88 
  89         check_sgerr(git_revparse_single(&tobject, grepo, name));
  90         gbase = git_object_id(tobject);
  91         git_object_free(tobject);
  92 }
  93 
  94 
  95 static void
  96 setup_git(void)
  97 {
  98         if (git_init)
  99                 return;
 100 
 101         git_libgit2_init();
 102 
 103         check_sgerr(chdir(query.self.rpath));
 104         git_repository_open_bare(&grepo, "./");
 105 
 106         if (!grepo)
 107                 check_sgerr(git_repository_open(&grepo, "./"));
 108 
 109         if (query.trev[0] != '\0')
 110                 switch_object(query.trev);
 111         else
 112                 switch_object("HEAD");
 113 
 114         git_init = 1;
 115 }
 116 
 117 
 118 void
 119 page_setup(int mini)
 120 {
 121         git_object *head;
 122         char new_clone_url[MINB] = {0};
 123         char description[MINB] = {0};
 124         char descfile[MINB] = {0};
 125         char idh[9] = {0};
 126         char *cp;
 127         FILE *df;
 128         int ishead = 0;
 129 
 130         if (has_page || binary_xfer)
 131                 return;
 132 
 133         has_page = 1;
 134 
 135         http_header_html();
 136 
 137         if (mini) {
 138                 html("\n");
 139                 html_page_begin(title);
 140                 html("<div class=\"git-main\">\n");
 141 
 142                 return;
 143         }
 144 
 145         setup_filters();
 146         http_header_end(query.can_compress);
 147 
 148         setup_git();
 149 
 150         check_sgerr(git_oid_nfmt(idh, 8, gbase));
 151         check_sgerr(git_revparse_single(&head, grepo, "HEAD"));
 152         ishead = git_oid_cmp(gbase, git_object_id(head)) == 0;
 153 
 154         snprintf(new_clone_url, MINB, "%s%s", clone_url, query.repn);
 155         snprintf(descfile, MINB, "%s/description", query.self.rpath);
 156 
 157         if ((df = fopen(descfile, "r"))) {
 158                 fgets(description, MINB, df);
 159                 fclose(df);
 160                 if ((cp = strrchr(description, '\n')))
 161                         *cp = '\0';
 162         }
 163 
 164         html_page_begin(title);
 165         html("<div class=\"git-main\">\n\
 166 <div class=\"git-header\">\n\
 167 <table>\n\
 168   <tr><td><h1>%s</h1> <span class=\"git-desc\">%s</span></td></tr>\n\
 169   <tr><td><code class=\"git-clone-cmd\">git clone %s</code></td></tr>\n\
 170   <tr><td class=\"git-nav\">%s | %s | %s | %s</td></tr>\n\
 171 </table>\n\
 172 <hr>\n\
 173 <span class=\"git-query-bar\"><code>%s<-%s%s%s: %s/<b>%s</b></code></span>\n\
 174 <hr>\n\
 175 </div>\n", rname, my_html_encode(description),
 176             my_html_make_link(new_clone_url, my_html_encode(new_clone_url)),
 177             my_html_make_link(make_query_simple(T_LOG, NULL, NULL),
 178     "Log"),
 179             my_html_make_link(make_query_simple(T_COMMIT, NULL, NULL),
 180     "Commit"),
 181             my_html_make_link(make_query_simple(T_TREE, NULL, NULL),
 182     "Tree"),
 183             my_html_make_link(make_query_simple(T_REFS, NULL, NULL),
 184     "Refs"),
 185             my_html_make_link(make_query_simple(0, git_object_id(head), NULL),
 186     "HEAD"),
 187             ishead ? "" : "<span class=\"git-qb-nothead\">",
 188             idh,
 189             ishead ? "" : "</span>",
 190             my_html_make_link(make_query_simple(0, NULL, "/"),
 191     "root"),
 192             query.path);
 193 }
 194 
 195 
 196 int
 197 check_gerr(int e, const char *op)
 198 {
 199         if (e >= 0) return e;
 200 
 201         page_setup(1);
 202         html_print_err("error %d, operation %s", e, op);
 203         cleanup(1);
 204 
 205         return e;
 206 }
 207 
 208 
 209 void *
 210 check_ptr(void *p, const char *op)
 211 {
 212         if (!p)
 213                 check_gerr(-1, op);
 214 
 215         return p;
 216 }
 217 
 218 
 219 char *
 220 make_query(struct q *dest)
 221 {
 222         char *qstr = check_sptr(my_malloc(MINB));
 223 
 224         snprintf(qstr, MINB, "?r=%s&path=%s&op=%c&id=%s",
 225             dest->repn, dest->path, dest->tool, dest->trev);
 226 
 227         return qstr;
 228 }
 229 
 230 
 231 char *
 232 make_query_simple(int t, const git_oid *id, const char *path)
 233 {
 234         struct q dest = query;
 235         char *p2, *pw = path
 236             ? my_malloc(html_catsz("rr", path, dest.path))
 237             : NULL;
 238         char idh[9] = {0};
 239 
 240         if (path) {
 241                 (void)check_sptr(pw);
 242                 if (*path != '/')
 243                         sprintf(pw, "%s/%s", dest.path, path);
 244                 else
 245                         sprintf(pw, "%s", path);
 246                 p2 = check_sptr(norm_path(pw, 0));
 247                 my_free(pw);
 248                 dest.path[0] = '\0';
 249                 strncat(dest.path, p2, MINL);
 250                 my_free(p2);
 251         }
 252 
 253         if (id) {
 254                 check_sgerr(git_oid_nfmt(idh, 8, id));
 255                 snprintf(dest.trev, MINB, "%s", idh);
 256         }
 257         if (t) dest.tool = t;
 258 
 259         return make_query(&dest);
 260 }
 261 
 262 
 263 char *
 264 make_snapshot_name(const char *ext)
 265 {
 266         char *snapname = check_sptr(my_malloc(MINB));
 267         char cidbuf[MINB];
 268 
 269         git_oid_tostr(cidbuf, MINB, gbase);
 270         snprintf(snapname, MINB, "%s-%s.%s", rname, cidbuf, ext);
 271 
 272         return snapname;
 273 }
 274 
 275 
 276 char *
 277 make_tag_summary(git_commit *commit)
 278 {
 279         const git_oid *cid = git_commit_id(commit);
 280         git_reference_iterator *iter;
 281         git_reference *ref;
 282         git_object *obj;
 283         char tagbuf[MINB];
 284         char *fgs = my_html_encode((char *)git_commit_summary(commit));
 285         size_t tsz = (strlen(fgs) + MINB);
 286         char *t = check_sptr(my_malloc(tsz + 1));
 287         *t = '\0';
 288 
 289         strncat(t, fgs, tsz);
 290 
 291         check_sgerr(git_revparse_single(&obj, grepo, "HEAD"));
 292         if (git_oid_cmp(git_object_id(obj), cid) == 0)
 293                 strncat(t,
 294                     " <span class=\"git-tag git-tag-head\">HEAD</span>", tsz);
 295         git_object_free(obj);
 296 
 297         check_sgerr(git_reference_iterator_glob_new(
 298             &iter, grepo, "refs/heads/*"));
 299         while (!git_reference_next(&ref, iter)) {
 300                 if (git_oid_cmp(git_reference_target(ref), cid) == 0) {
 301                         snprintf(tagbuf, MINB,
 302                             " <span class=\"git-tag git-tag-branch\">\
 303 %s</span>", (char *)my_html_encode(git_reference_shorthand(ref)));
 304                         strncat(t, tagbuf, tsz);
 305                 }
 306                 git_reference_free(ref);
 307         }
 308         git_reference_iterator_free(iter);
 309 
 310         my_free(fgs);
 311 
 312         return t;
 313 }
 314 
 315 
 316 git_diff *
 317 commit_to_diff(git_commit *commit, git_commit *parent)
 318 {
 319         git_diff *diff;
 320         git_tree *commit_tree, *parent_tree = NULL;
 321         git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
 322 
 323         check_sgerr(git_commit_tree(&commit_tree, commit));
 324         if (parent) {
 325                 git_commit_tree(&parent_tree, parent);
 326         }
 327 
 328         check_sgerr(git_diff_tree_to_tree(
 329             &diff, grepo, parent_tree, commit_tree, NULL));
 330 
 331         opts.flags = GIT_DIFF_FIND_RENAMES
 332             | GIT_DIFF_FIND_COPIES
 333             | GIT_DIFF_FIND_FOR_UNTRACKED;
 334 
 335         check_sgerr(git_diff_find_similar(diff, &opts));
 336 
 337         git_tree_free(commit_tree);
 338         if (parent_tree) git_tree_free(parent_tree);
 339 
 340         return diff;
 341 }
 342 
 343 
 344 static void
 345 do_query(const char *name, const char *value)
 346 {
 347         char *pw;
 348 
 349         if (strcasecmp("id", name) == 0 && value)
 350                 strncat(query.trev, value, MINL);
 351         else if (strcasecmp("path", name) == 0 && value)
 352                 strncat(query.path, value, MINL);
 353         else if (strcasecmp("op", name) == 0 && value)
 354                 query.tool = *value;
 355         else if (!repo && strcasecmp("r", name) == 0 && value) {
 356                 pw = check_sptr(norm_path(value, 1));
 357                 strncat(query.repn, pw, MINL);
 358                 my_free(pw);
 359         }
 360 }
 361 
 362 
 363 static void
 364 env_setup(void)
 365 {
 366         char *cp, *cp2, *cp3, *name, *value;
 367 
 368         /* XXX: called before git repo open */
 369 
 370         if (repo) strncat(query.self.rpath, repo, MINL);
 371 
 372         if(!(cp = getenv("QUERY_STRING")))  goto query_skip;
 373         if(!(cp = strtok_r(cp, "&", &cp2))) goto query_skip;
 374 
 375         do {
 376                 if ((name = strtok_r(cp, "=", &cp3)))
 377                         value = strtok_r(NULL, "=", &cp3);
 378                 do_query(name, value);
 379         } while ((cp = strtok_r(NULL, "&", &cp2)) != NULL);
 380 
 381  query_skip:
 382         if (!repo)
 383                 if (query.repn[0] != '\0')
 384                         snprintf(query.self.rpath, MINB,
 385                             "%s%s", repo_dir, query.repn);
 386                 else
 387                         check_gerr(-1, "null repository");
 388 
 389         cp  = NULL;
 390 #define try_p(...)                                      \
 391 if (!cp) {                                              \
 392         do {__VA_ARGS__} while(0);                      \
 393         cp3 = cp = realpath(query.self.rpath, NULL); }
 394         try_p( /* default */ );
 395         try_p(
 396             cp2 = strstr(query.self.rpath, ".git");
 397             if (cp2) *cp2 = '\0'; );
 398         try_p(
 399             strncat(query.self.rpath, ".git", MINL); );
 400         if (!cp) {
 401                 snprintf(title, MINB, "(git) error");
 402                 check_gerr(-1, "realpath");
 403 
 404                 return;
 405         }
 406 
 407         cp2 = strrchr(cp, '/');
 408         cp  = cp2 && *(cp2 + 1) != '\0' ? cp2 + 1 : cp;
 409 
 410         strncat(rname, cp, MINL);
 411         snprintf(title, MINB, "(git) %s", rname);
 412 
 413         free(cp3);
 414 
 415         if (repo) snprintf(query.repn, MINB, "/%s", rname);
 416 
 417         if (!(query.tool && strchr("lctbrpzZ", query.tool)))
 418                 query.tool = T_LOG;
 419 
 420         cp = check_sptr(norm_path(query.path, 0));
 421         memset(query.path, '\0', MINB);
 422         strncat(query.path, cp, MINL);
 423 }
 424 
 425 
 426 static void
 427 do_archive(char *fmt)
 428 {
 429         pid_t p;
 430         char cmdbuf[MINB], cidbuf[MINB];
 431 
 432         binary_xfer = 1;
 433         http_header_attachment(1, make_snapshot_name(fmt));
 434         http_header_end(query.can_compress);
 435 
 436         git_oid_tostr(cidbuf, MINB, gbase);
 437         fflush(stdout);
 438         p = check_sgerr(fork());
 439         if (p == 0) {
 440                 snprintf(cmdbuf, MINB, "--format=%s", fmt);
 441                 execlp("git", "git", "archive", cmdbuf, cidbuf, (char *) NULL);
 442         } else  waitpid(p, NULL, 0);
 443 }
 444 
 445 
 446 int main(int argc, char **argv)
 447 {
 448         struct stat sb;
 449 
 450         /* XXX: just write to stdout, we'll adjust it later */
 451         capture = stdout;
 452 
 453         env_setup();
 454 
 455         strncat(query.self.mpath, argv[0], MINL);
 456         if (stat(query.self.mpath, &sb) >= 0)
 457                 query.self.mtime = sb.st_mtim;
 458         if (stat(query.self.rpath, &sb) >= 0)
 459                 query.self.rtime = sb.st_mtim;
 460 
 461         if (T_IS(T_USES_PAGE))
 462                 page_setup(0);
 463         else
 464                 setup_filters();
 465 
 466         setup_git(); /* XXX: if we haven't already... (see page_setup) */
 467 
 468         switch (query.tool) {
 469         case T_LOG:     dump_commits(); break;
 470         case T_PATCH:
 471         case T_COMMIT:  dump_commit();  break;
 472         case T_BLOB:
 473         case T_TREE:    dump_tree();    break;
 474         case T_REFS:    dump_refs();    break;
 475         case T_TARGZ:   do_archive("tgz");      break;
 476         case T_ZIP:     do_archive("zip");      break;
 477         }
 478 
 479         cleanup(0);
 480         return 0; /* never achieved */
 481 }

/* [previous][next][first][last][top][bottom][index][help] */