]> gitweb.ps.run Git - ps-cgit/commitdiff
Merge branch 'jo/dirlink'
authorLars Hjemli <hjemli@gmail.com>
Mon, 3 Sep 2007 21:00:06 +0000 (23:00 +0200)
committerLars Hjemli <hjemli@gmail.com>
Mon, 3 Sep 2007 21:00:06 +0000 (23:00 +0200)
* jo/dirlink:
  Rename dirlink to gitlink.

Conflicts:

ui-tree.c

Signed-off-by: Lars Hjemli <hjemli@gmail.com>
22 files changed:
.gitignore
.gitmodules
Makefile
cgit.c
cgit.css
cgit.h
cgitrc
gen-version.sh [new file with mode: 0755]
git
parsing.c
shared.c
submodules.sh [deleted file]
ui-commit.c
ui-diff.c
ui-log.c
ui-repolist.c
ui-shared.c
ui-snapshot.c
ui-summary.c
ui-tag.c [new file with mode: 0644]
ui-tree.c
ui-view.c [deleted file]

index c4c9ac3ffa5af077a3a5ffd349a413df1f6e703b..566496210185cd3667c8364649d87f4f730c822b 100644 (file)
@@ -1,4 +1,5 @@
 # Files I don't care to see in git-status/commit
 cgit
+VERSION
 *.o
 *~
index 51dd1eff1edc663674df9ab85d2786a40f7ae3a5..1daea942e6eeb393d0a8af2355a97537faf113ad 100644 (file)
@@ -1,5 +1,3 @@
-# This file maps a submodule path to an url from where the submodule
-# can be obtained. The script "submodules.sh" finds the url in this file
-# when invoked with -i to clone the submodules.
-
-git            git://git.kernel.org/pub/scm/git/git.git
+[submodule "git"]
+       url = git://git.kernel.org/pub/scm/git/git.git
+       path = git
index 57f80f826d67aff2280904d8a7f8efb7e0c09ed3..fcbe3e47e22b7caf0815e1296692ca794c30daac 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,87 +1,71 @@
-CGIT_VERSION = 0.5
-
-prefix = /var/www/htdocs/cgit
-
-SHA1_HEADER = <openssl/sha.h>
-CACHE_ROOT = /var/cache/cgit
-CGIT_CONFIG = /etc/cgitrc
+CGIT_VERSION = v0.5
 CGIT_SCRIPT_NAME = cgit.cgi
+CGIT_SCRIPT_PATH = /var/www/htdocs/cgit
+CGIT_CONFIG = /etc/cgitrc
+CACHE_ROOT = /var/cache/cgit
+SHA1_HEADER = <openssl/sha.h>
+GIT_VER = 1.5.2
+GIT_URL = http://www.kernel.org/pub/software/scm/git/git-$(GIT_VER).tar.bz2
 
 #
 # Let the user override the above settings.
 #
 -include cgit.conf
 
+
 EXTLIBS = git/libgit.a git/xdiff/lib.a -lz -lcrypto
 OBJECTS = shared.o cache.o parsing.o html.o ui-shared.o ui-repolist.o \
-       ui-summary.o ui-log.o ui-view.o ui-tree.o ui-commit.o ui-diff.o \
-       ui-snapshot.o ui-blob.o
+       ui-summary.o ui-log.o ui-tree.o ui-commit.o ui-diff.o \
+       ui-snapshot.o ui-blob.o ui-tag.o
+
+
+.PHONY: all git install clean distclean force-version get-git
 
-CFLAGS += -Wall
+all: cgit git
 
-ifdef DEBUG
-       CFLAGS += -g
-endif
+VERSION: force-version
+       @./gen-version.sh "$(CGIT_VERSION)"
+-include VERSION
 
-CFLAGS += -Igit
+
+CFLAGS += -g -Wall -Igit
 CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER)'
 CFLAGS += -DCGIT_VERSION='"$(CGIT_VERSION)"'
 CFLAGS += -DCGIT_CONFIG='"$(CGIT_CONFIG)"'
 CFLAGS += -DCGIT_SCRIPT_NAME='"$(CGIT_SCRIPT_NAME)"'
 
 
-#
-# If make is run on a nongit platform, get the git sources as a tarball.
-#
-GITVER = $(shell git version 2>/dev/null || echo nogit)
-ifeq ($(GITVER),nogit)
-GITURL = http://www.kernel.org/pub/software/scm/git/git-1.5.2.tar.bz2
-INITGIT = test -e git/git.c || ((curl "$(GITURL)" | tar -xj) && mv git-1.5.2 git)
-else
-INITGIT = ./submodules.sh -i
-endif
-
-
-#
-# basic build rules
-#
-all: cgit
-
-cgit: cgit.c cgit.h $(OBJECTS)
+cgit: cgit.c $(OBJECTS)
        $(CC) $(CFLAGS) cgit.c -o cgit $(OBJECTS) $(EXTLIBS)
 
-$(OBJECTS): cgit.h git/libgit.a
+$(OBJECTS): cgit.h git/xdiff/lib.a git/libgit.a VERSION
 
-git/libgit.a:
-       $(INITGIT)
-       $(MAKE) -C git
+git/xdiff/lib.a: | git
 
-#
-# phony targets
-#
-install: all clean-cache
-       mkdir -p $(prefix)
-       install cgit $(prefix)/$(CGIT_SCRIPT_NAME)
-       install cgit.css $(prefix)/cgit.css
+git/libgit.a: | git
 
-clean-cgit:
-       rm -f cgit *.o
+git:
+       cd git && $(MAKE) xdiff/lib.a
+       cd git && $(MAKE) libgit.a
 
-distclean-cgit: clean-cgit
-       git clean -d -x
-
-clean-sub:
-       $(MAKE) -C git clean
-
-distclean-sub: clean-sub
-       $(shell cd git && git clean -d -x)
-
-clean-cache:
+install: all
+       mkdir -p $(CGIT_SCRIPT_PATH)
+       install cgit $(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME)
+       install cgit.css $(CGIT_SCRIPT_PATH)/cgit.css
        rm -rf $(CACHE_ROOT)/*
 
-clean: clean-cgit clean-sub
+uninstall:
+       rm -f $(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME)
+       rm -f $(CGIT_SCRIPT_PATH)/cgit.css
+       rm -rf $(CACHE_ROOT)
+
+clean:
+       rm -f cgit VERSION *.o
+       cd git && $(MAKE) clean
 
-distclean: distclean-cgit distclean-sub
+distclean: clean
+       git clean -d -x
+       cd git && git clean -d -x
 
-.PHONY: all install clean clean-cgit clean-sub clean-cache \
-       distclean distclean-cgit distclean-sub
+get-git:
+       curl $(GIT_URL) | tar -xj && rm -rf git && mv git-$(GIT_VER) git
diff --git a/cgit.c b/cgit.c
index 34e590e79dca789d560b308f0b1865f36e4df3f4..c86d290bce3477125409b3b75cc0d552618b5589 100644 (file)
--- a/cgit.c
+++ b/cgit.c
@@ -8,9 +8,6 @@
 
 #include "cgit.h"
 
-const char cgit_version[] = CGIT_VERSION;
-
-
 static int cgit_prepare_cache(struct cacheitem *item)
 {
        if (!cgit_repo && cgit_query_repo) {
@@ -29,13 +26,15 @@ static int cgit_prepare_cache(struct cacheitem *item)
        }
 
        if (!cgit_cmd) {
-               item->name = xstrdup(fmt("%s/%s/index.html", cgit_cache_root,
-                          cache_safe_filename(cgit_repo->url)));
+               item->name = xstrdup(fmt("%s/%s/index.%s.html", cgit_cache_root,
+                                        cache_safe_filename(cgit_repo->url),
+                                        cache_safe_filename(cgit_querystring)));
                item->ttl = cgit_cache_repo_ttl;
        } else {
                item->name = xstrdup(fmt("%s/%s/%s/%s.html", cgit_cache_root,
-                          cache_safe_filename(cgit_repo->url), cgit_query_page,
-                          cache_safe_filename(cgit_querystring)));
+                                        cache_safe_filename(cgit_repo->url),
+                                        cgit_query_page,
+                                        cache_safe_filename(cgit_querystring)));
                if (cgit_query_has_symref)
                        item->ttl = cgit_cache_dynamic_ttl;
                else if (cgit_query_has_sha1)
@@ -69,8 +68,10 @@ static void cgit_print_repo_page(struct cacheitem *item)
        setenv("GIT_DIR", cgit_repo->path, 1);
 
        if ((cgit_cmd == CMD_SNAPSHOT) && cgit_repo->snapshots) {
-               cgit_print_snapshot(item, cgit_query_sha1, "zip",
-                                   cgit_repo->url, cgit_query_name);
+               cgit_print_snapshot(item, cgit_query_head, cgit_query_sha1,
+                                   cgit_repobasename(cgit_repo->url),
+                                   cgit_query_path,
+                                   cgit_repo->snapshots );
                return;
        }
 
@@ -92,22 +93,21 @@ static void cgit_print_repo_page(struct cacheitem *item)
 
        switch(cgit_cmd) {
        case CMD_LOG:
-               cgit_print_log(cgit_query_head, cgit_query_ofs,
+               cgit_print_log(cgit_query_sha1, cgit_query_ofs,
                               cgit_max_commit_count, cgit_query_search,
                               cgit_query_path, 1);
                break;
        case CMD_TREE:
-               cgit_print_tree(cgit_query_head, cgit_query_sha1, cgit_query_path);
+               cgit_print_tree(cgit_query_sha1, cgit_query_path);
                break;
        case CMD_COMMIT:
-               cgit_print_commit(cgit_query_head);
+               cgit_print_commit(cgit_query_sha1);
                break;
-       case CMD_VIEW:
-               cgit_print_view(cgit_query_sha1, cgit_query_path);
+       case CMD_TAG:
+               cgit_print_tag(cgit_query_sha1);
                break;
        case CMD_DIFF:
-               cgit_print_diff(cgit_query_head, cgit_query_sha1, cgit_query_sha2,
-                               cgit_query_path);
+               cgit_print_diff(cgit_query_sha1, cgit_query_sha2);
                break;
        default:
                cgit_print_error("Invalid request");
@@ -227,6 +227,7 @@ static void cgit_parse_args(int argc, const char **argv)
 int main(int argc, const char **argv)
 {
        struct cacheitem item;
+       const char *cgit_config_env = getenv("CGIT_CONFIG");
 
        htmlfd = STDOUT_FILENO;
        item.st.st_mtime = time(NULL);
@@ -234,7 +235,8 @@ int main(int argc, const char **argv)
        cgit_repolist.count = 0;
        cgit_repolist.repos = NULL;
 
-       cgit_read_config(CGIT_CONFIG, cgit_global_config_cb);
+       cgit_read_config(cgit_config_env ? cgit_config_env : CGIT_CONFIG,
+                        cgit_global_config_cb);
        cgit_repo = NULL;
        if (getenv("SCRIPT_NAME"))
                cgit_script_name = xstrdup(getenv("SCRIPT_NAME"));
index 8977533a18e8a6c5ceb16d4742906fd4b0bd75e0..54bbfccb421faf0c35f6eb4749ccef40f9e88d06 100644 (file)
--- a/cgit.css
+++ b/cgit.css
@@ -1,6 +1,7 @@
 body {
-       font-family: arial;
+       font-family: arial, sans-serif;
        font-size: 11pt;
+       color: black;
        background: white;
 }
 
@@ -94,6 +95,14 @@ td#header {
        vertical-align: text-bottom;
 }
 
+td#header a {
+       color: #666;
+}
+
+td#header a:hoved {
+       text-decoration: underline;
+}
+
 td#logo {
        text-align: right;
        vertical-align: middle;
@@ -114,15 +123,19 @@ td#crumb {
 
 td#crumb a {
        color: #ccc;
+       background-color: #666;
+       padding: 0em 0.5em 0em 0.5em;
 }
 
 td#crumb a:hover {
-       color: #eee;
+       color: #666;
+       background-color: #ccc;
+       text-decoration: none;
 }
 
 td#search {
        text-align: right;
-       vertical-align: center;
+       vertical-align: middle;
        padding-right: 0.5em;
 }
 
@@ -171,35 +184,47 @@ div.error {
        margin: 1em 2em;
 }
 
-td.ls-blob, td.ls-dir, td.ls-mod {
+a.ls-blob, a.ls-dir, a.ls-mod {
        font-family: monospace;
 }
 
-div.ls-dir a {
-       font-weight: bold;
+td.ls-size {
+       text-align: right;
 }
 
-th.filesize, td.filesize {
-       text-align: right;
+td.ls-size {
+       font-family: monospace;
 }
 
-td.filesize {
+td.ls-mode {
        font-family: monospace;
 }
 
-td.links {
-       font-size: 80%;
-       padding-left: 2em;
+table.blob {
+       margin-top: 0.5em;
+       border-top: solid 1px black;
 }
 
-td.filemode {
-       font-family: monospace;
+table.blob td.no {
+       border-right: solid 1px black;
+       color: black;
+       background-color: #eee;
+       text-align: right;
+}
+
+table.blob td.no a {
+       color: black;
 }
 
-td.blob {
+table.blob td.no a:hover {
+       color: black;
+       text-decoration: none;
+}
+
+table.blob td.txt {
        white-space: pre;
        font-family: monospace;
-       background-color: white;
+       padding-left: 0.5em;
 }
 
 table.nowrap td {
@@ -215,6 +240,7 @@ table.commit-info th {
        text-align: left;
        font-weight: normal;
        padding: 0.1em 1em 0.1em 0.1em;
+       vertical-align: top;
 }
 
 table.commit-info td {
@@ -287,7 +313,7 @@ table.diffstat td.upd a {
 
 table.diffstat td.graph {
        width: 75%;
-       vertical-align: center;
+       vertical-align: middle;
 }
 
 table.diffstat td.graph table {
@@ -308,10 +334,6 @@ table.diffstat td.graph td.rem {
        background-color: #c55;
 }
 
-table.diffstat td.graph td.none {
-       background-color: none;
-}
-
 div.diffstat-summary {
        color: #888;
        padding-top: 0.5em;
@@ -340,7 +362,7 @@ table.diff td div.del {
 }
 
 .sha1 {
-       font-family: courier;
+       font-family: monospace;
        font-size: 90%;
 }
 
@@ -359,16 +381,17 @@ table.list td.repogroup {
 
 a.button {
        font-size: 80%;
-       color: #333;
-       background-color: #ccc;
-       border: solid 1px #999;
+       color: #aaa;
+       background-color: #eee;
+       border: solid 1px #aaa;
        padding: 0em 0.5em;
        margin: 0.1em 0.25em;
 }
 
 a.button:hover {
        text-decoration: none;
-       background-color: #eee;
+       color: #333;
+       background-color: #ccc;
 }
 
 a.primary {
diff --git a/cgit.h b/cgit.h
index 2f3fca18fe0f3cb0a8742ae262c06056e3caf25f..e3d9cb875171de19c098e79f8b19231e37aba5b2 100644 (file)
--- a/cgit.h
+++ b/cgit.h
 #define CMD_COMMIT   2
 #define CMD_DIFF     3
 #define CMD_TREE     4
-#define CMD_VIEW     5
-#define CMD_BLOB     6
-#define CMD_SNAPSHOT 7
-
+#define CMD_BLOB     5
+#define CMD_SNAPSHOT 6
+#define CMD_TAG      7
 
 /*
  * Dateformats used on misc. pages
@@ -99,7 +98,7 @@ struct taginfo {
        char *msg;
 };
 
-extern const char cgit_version[];
+extern const char *cgit_version;
 
 extern struct repolist cgit_repolist;
 extern struct repoinfo *cgit_repo;
@@ -119,6 +118,7 @@ extern char *cgit_repo_group;
 
 extern int cgit_nocache;
 extern int cgit_snapshots;
+extern int cgit_enable_index_links;
 extern int cgit_enable_log_filecount;
 extern int cgit_enable_log_linecount;
 extern int cgit_max_lock_attempts;
@@ -157,8 +157,10 @@ extern void cgit_querystring_cb(const char *name, const char *value);
 
 extern int chk_zero(int result, char *msg);
 extern int chk_positive(int result, char *msg);
+extern int chk_non_negative(int result, char *msg);
 
 extern int hextoint(char c);
+extern char *trim_end(const char *str, char c);
 
 extern void *cgit_free_commitinfo(struct commitinfo *info);
 
@@ -199,9 +201,26 @@ extern int cache_exist(struct cacheitem *item);
 extern int cache_expired(struct cacheitem *item);
 
 extern char *cgit_repourl(const char *reponame);
+extern char *cgit_fileurl(const char *reponame, const char *pagename,
+                         const char *filename, const char *query);
 extern char *cgit_pageurl(const char *reponame, const char *pagename,
                          const char *query);
 
+extern const char *cgit_repobasename(const char *reponame);
+
+extern void cgit_tree_link(char *name, char *title, char *class, char *head,
+                          char *rev, char *path);
+extern void cgit_log_link(char *name, char *title, char *class, char *head,
+                         char *rev, char *path, int ofs);
+extern void cgit_commit_link(char *name, char *title, char *class, char *head,
+                            char *rev);
+extern void cgit_snapshot_link(char *name, char *title, char *class,
+                              char *head, char *rev, char *archivename);
+extern void cgit_diff_link(char *name, char *title, char *class, char *head,
+                          char *new_rev, char *old_rev, char *path);
+
+extern void cgit_object_link(struct object *obj);
+
 extern void cgit_print_error(char *msg);
 extern void cgit_print_date(time_t secs, char *format);
 extern void cgit_print_age(time_t t, time_t max_relative, char *format);
@@ -215,14 +234,16 @@ extern void cgit_print_snapshot_start(const char *mimetype,
 extern void cgit_print_repolist(struct cacheitem *item);
 extern void cgit_print_summary();
 extern void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *path, int pager);
-extern void cgit_print_view(const char *hex, char *path);
 extern void cgit_print_blob(struct cacheitem *item, const char *hex, char *path);
-extern void cgit_print_tree(const char *rev, const char *hex, char *path);
-extern void cgit_print_commit(const char *hex);
-extern void cgit_print_diff(const char *head, const char *old_hex, const char *new_hex,
-                           char *path);
-extern void cgit_print_snapshot(struct cacheitem *item, const char *hex,
-                               const char *format, const char *prefix,
-                               const char *filename);
+extern void cgit_print_tree(const char *rev, char *path);
+extern void cgit_print_commit(char *hex);
+extern void cgit_print_tag(char *revname);
+extern void cgit_print_diff(const char *new_hex, const char *old_hex);
+extern void cgit_print_snapshot(struct cacheitem *item, const char *head,
+                               const char *hex, const char *prefix,
+                               const char *filename, int snapshot);
+extern void cgit_print_snapshot_links(const char *repo, const char *head,
+                                     const char *hex, int snapshots);
+extern int cgit_parse_snapshots_mask(const char *str);
 
 #endif /* CGIT_H */
diff --git a/cgitrc b/cgitrc
index 0f602e471a73e177d2a15643fd539f5204ad773a..1040997d976aea817c410e3cfb788415a2d6bf46 100644 (file)
--- a/cgitrc
+++ b/cgitrc
@@ -8,10 +8,15 @@
 #nocache=0
 
 
-## Enable/disable snapshots by default. This can be overridden per repo
+## Set allowed snapshot types by default. Can be overridden per repo
+# can be any combination of zip/tar.gz/tar.bz2/tar
 #snapshots=0
 
 
+## Enable/disable extra links to summary/log/tree per repo on index page
+#enable-index-links=0
+
+
 ## Enable/disable display of 'number of files changed' in log view
 #enable-log-filecount=0
 
 #repo.desc=the caching cgi for git
 #repo.path=/pub/git/cgit
 #repo.owner=Lars Hjemli
-#repo.snapshots=1                              # override a sitewide snapshot-setting
+#repo.snapshots=tar.bz2                                # override a sitewide snapshot-setting
 #repo.enable-log-filecount=0                   # override the default filecount setting
 #repo.enable-log-linecount=0                   # override the default linecount setting
 #repo.module-link=/git/%s/commit/?id=%s                # override the standard module-link
diff --git a/gen-version.sh b/gen-version.sh
new file mode 100755 (executable)
index 0000000..739c83e
--- /dev/null
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+# Get version-info specified in Makefile
+V=$1
+
+# Use `git describe` to get current version if we're inside a git repo
+if test -d .git
+then
+       V=$(git describe --abbrev=4 HEAD 2>/dev/null | sed -e 's/-/./g')
+fi
+
+new="CGIT_VERSION = $V"
+old=$(cat VERSION 2>/dev/null)
+
+# Exit if VERSION is uptodate
+test "$old" = "$new" && exit 0
+
+# Update VERSION with new version-info
+echo "$new" > VERSION
+cat VERSION
diff --git a/git b/git
index aba170cdb4874b72dd619e6f7bbc13c33295f831..86bab9615c3516d4ac7756ae3c1285d331b78f04 160000 (submodule)
--- a/git
+++ b/git
@@ -1 +1 @@
-Subproject commit aba170cdb4874b72dd619e6f7bbc13c33295f831
+Subproject commit 86bab9615c3516d4ac7756ae3c1285d331b78f04
index 74a248449f35fb2a5331f571d3bea30a2806acab..2c05c0939ff34547ad6f6ee71fb37cbdc30066c8 100644 (file)
--- a/parsing.c
+++ b/parsing.c
@@ -168,7 +168,7 @@ void cgit_parse_url(const char *url)
                if (p) {
                        p[0] = '\0';
                        if (p[1])
-                               cgit_query_path = xstrdup(p + 1);
+                               cgit_query_path = trim_end(p + 1, '/');
                }
                cgit_cmd = cgit_get_cmd_index(cmd + 1);
                cgit_query_page = xstrdup(cmd + 1);
index b6d2fa1efe6a47f93054d6a6af29c3b4a5dbbb03..077934f7ad7a51359d64d92407647232e83904f2 100644 (file)
--- a/shared.c
+++ b/shared.c
@@ -12,6 +12,8 @@ struct repolist cgit_repolist;
 struct repoinfo *cgit_repo;
 int cgit_cmd;
 
+const char *cgit_version = CGIT_VERSION;
+
 char *cgit_root_title   = "Git repository browser";
 char *cgit_css          = "/cgit.css";
 char *cgit_logo         = "/git-logo.png";
@@ -26,6 +28,7 @@ char *cgit_repo_group   = NULL;
 
 int cgit_nocache               =  0;
 int cgit_snapshots             =  0;
+int cgit_enable_index_links    =  0;
 int cgit_enable_log_filecount  =  0;
 int cgit_enable_log_linecount  =  0;
 int cgit_max_lock_attempts     =  5;
@@ -59,7 +62,8 @@ int htmlfd = 0;
 
 int cgit_get_cmd_index(const char *cmd)
 {
-       static char *cmds[] = {"log", "commit", "diff", "tree", "view", "blob", "snapshot", NULL};
+       static char *cmds[] = {"log", "commit", "diff", "tree", "blob",
+                              "snapshot", "tag", NULL};
        int i;
 
        for(i = 0; cmds[i]; i++)
@@ -82,6 +86,13 @@ int chk_positive(int result, char *msg)
        return result;
 }
 
+int chk_non_negative(int result, char *msg)
+{
+       if (result < 0)
+               die("%s: %s",msg, strerror(errno));
+       return result;
+}
+
 struct repoinfo *add_repo(const char *url)
 {
        struct repoinfo *ret;
@@ -144,7 +155,9 @@ void cgit_global_config_cb(const char *name, const char *value)
        else if (!strcmp(name, "nocache"))
                cgit_nocache = atoi(value);
        else if (!strcmp(name, "snapshots"))
-               cgit_snapshots = atoi(value);
+               cgit_snapshots = cgit_parse_snapshots_mask(value);
+       else if (!strcmp(name, "enable-index-links"))
+               cgit_enable_index_links = atoi(value);
        else if (!strcmp(name, "enable-log-filecount"))
                cgit_enable_log_filecount = atoi(value);
        else if (!strcmp(name, "enable-log-linecount"))
@@ -184,7 +197,7 @@ void cgit_global_config_cb(const char *name, const char *value)
        else if (cgit_repo && !strcmp(name, "repo.defbranch"))
                cgit_repo->defbranch = xstrdup(value);
        else if (cgit_repo && !strcmp(name, "repo.snapshots"))
-               cgit_repo->snapshots = cgit_snapshots * atoi(value);
+               cgit_repo->snapshots = cgit_snapshots & cgit_parse_snapshots_mask(value); /* XXX: &? */
        else if (cgit_repo && !strcmp(name, "repo.enable-log-filecount"))
                cgit_repo->enable_log_filecount = cgit_enable_log_filecount * atoi(value);
        else if (cgit_repo && !strcmp(name, "repo.enable-log-linecount"))
@@ -224,7 +237,7 @@ void cgit_querystring_cb(const char *name, const char *value)
        } else if (!strcmp(name, "ofs")) {
                cgit_query_ofs = atoi(value);
        } else if (!strcmp(name, "path")) {
-               cgit_query_path = xstrdup(value);
+               cgit_query_path = trim_end(value, '/');
        } else if (!strcmp(name, "name")) {
                cgit_query_name = xstrdup(value);
        }
@@ -253,6 +266,28 @@ int hextoint(char c)
                return -1;
 }
 
+char *trim_end(const char *str, char c)
+{
+       int len;
+       char *s, *t;
+
+       if (str == NULL)
+               return NULL;
+       t = (char *)str;
+       len = strlen(t);
+       while(len > 0 && t[len - 1] == c)
+               len--;
+
+       if (len == 0)
+               return NULL;
+
+       c = t[len];
+       t[len] = '\0';
+       s = xstrdup(t);
+       t[len] = c;
+       return s;
+}
+
 void cgit_diff_tree_cb(struct diff_queue_struct *q,
                       struct diff_options *options, void *data)
 {
@@ -359,7 +394,7 @@ void cgit_diff_tree(const unsigned char *old_sha1,
        opt.format_callback_data = fn;
        diff_setup_done(&opt);
 
-       if (old_sha1)
+       if (old_sha1 && !is_null_sha1(old_sha1))
                ret = diff_tree_sha1(old_sha1, new_sha1, "", &opt);
        else
                ret = diff_root_tree_sha1(new_sha1, "", &opt);
diff --git a/submodules.sh b/submodules.sh
deleted file mode 100755 (executable)
index 1d7b13f..0000000
+++ /dev/null
@@ -1,181 +0,0 @@
-#!/bin/sh
-#
-# submodules.sh: init, update or list git submodules
-#
-# Copyright (C) 2006 Lars Hjemli
-#
-# Licensed under GNU General Public License v2
-#   (see COPYING for full license text)
-#
-
-
-usage="submodules.sh [-i | -u] [-q] [--cached] [path...]"
-init=
-update=
-quiet=
-cached=
-
-
-say()
-{
-       if test -z "$quiet"
-       then
-               echo -e "$@"
-       fi
-}
-
-
-die()
-{
-       echo >&2 -e "$@"
-       exit 1
-}
-
-
-
-#
-# Silently checkout specified submodule revision, return exit status of git-checkout
-#
-# $1 = local path
-# $2 = requested sha1
-#
-module_checkout()
-{
-       $(cd "$1" && git checkout "$2" 1>/dev/null 2>/dev/null)
-}
-
-
-#
-# Find all (requested) submodules, run clone + checkout on missing paths
-#
-# $@ = requested paths (default to all)
-#
-modules_init()
-{
-       git ls-files --stage -- $@ | grep -e '^160000 ' |
-       while read mode sha1 stage path
-       do
-               test -d "$path/.git" && continue
-
-               if test -d "$path"
-               then
-                       rmdir "$path" 2>/dev/null ||
-                       die "Directory '$path' exist, but not as a submodule"
-               fi
-
-               test -e "$path" && die "A file already exist at path '$path'"
-
-               url=$(sed -nre "s/^$path[ \t]+//p" .gitmodules)
-               test -z "$url" && die "No url found for $path in .gitmodules"
-
-               git clone "$url" "$path" || die "Clone of submodule '$path' failed"
-               module_checkout "$path" "$sha1" || die "Checkout of submodule '$path' failed"
-               say "Submodule '$path' initialized"
-       done
-}
-
-#
-# Checkout correct revision of each initialized submodule
-#
-# $@ = requested paths (default to all)
-#
-modules_update()
-{
-       git ls-files --stage -- $@ | grep -e '^160000 ' |
-       while read mode sha1 stage path
-       do
-               if ! test -d "$path/.git"
-               then
-                       say "Submodule '$path' not initialized"
-                       continue;
-               fi
-               subsha1=$(cd "$path" && git rev-parse --verify HEAD) ||
-               die "Unable to find current revision of submodule '$path'"
-               if test "$subsha1" != "$sha1"
-               then
-                       module_checkout "$path" "$sha1" ||
-                       die "Unable to checkout revision $sha1 of submodule '$path'"
-                       say "Submodule '$path' reset to revision $sha1"
-               fi
-       done
-}
-
-#
-# List all registered submodules, prefixed with:
-#  - submodule not initialized
-#  + different version checked out
-#
-# If --cached was specified the revision in the index will be printed
-# instead of the currently checked out revision.
-#
-# $@ = requested paths (default to all)
-#
-modules_list()
-{
-       git ls-files --stage -- $@ | grep -e '^160000 ' |
-       while read mode sha1 stage path
-       do
-               if ! test -d "$path/.git"
-               then
-                       say "-$sha1 $path"
-                       continue;
-               fi
-               revname=$(cd "$path" && git describe $sha1)
-               if git diff-files --quiet -- "$path"
-               then
-                       say " $sha1 $path\t($revname)"
-               else
-                       if test -z "$cached"
-                       then
-                               sha1=$(cd "$path" && git rev-parse HEAD)
-                               revname=$(cd "$path" && git describe HEAD)
-                       fi
-                       say "+$sha1 $path\t($revname)"
-               fi
-       done
-}
-
-
-while case "$#" in 0) break ;; esac
-do
-       case "$1" in
-       -i)
-               init=1
-               ;;
-       -u)
-               update=1
-               ;;
-       -q)
-               quiet=1
-               ;;
-       --cached)
-               cached=1
-               ;;
-       --)
-               break
-               ;;
-       -*)
-               echo "Usage: $usage"
-               exit 1
-               ;;
-       --*)
-               echo "Usage: $usage"
-               exit 1
-               ;;
-       *)
-               break
-               ;;
-       esac
-       shift
-done
-
-
-if test "$init" = "1"
-then
-       modules_init $@
-elif test "$update" = "1"
-then
-       modules_update $@
-else
-       modules_list $@
-fi
index 1d12bbbcaf4a0ab5b7d7fd9c033aa529c099498e..90e09edb99e3e5df331f22ada90ad7be2391b386 100644 (file)
@@ -11,6 +11,7 @@
 static int files, slots;
 static int total_adds, total_rems, max_changes;
 static int lines_added, lines_removed;
+static char *curr_rev;
 
 static struct fileinfo {
        char status;
@@ -27,7 +28,6 @@ static struct fileinfo {
 
 void print_fileinfo(struct fileinfo *info)
 {
-       char *query, *query2;
        char *class;
 
        switch (info->status) {
@@ -75,24 +75,12 @@ void print_fileinfo(struct fileinfo *info)
                html("]</span>");
        }
        htmlf("</td><td class='%s'>", class);
-       query = fmt("id=%s&amp;id2=%s&amp;path=%s", sha1_to_hex(info->old_sha1),
-                   sha1_to_hex(info->new_sha1), info->new_path);
-       html_link_open(cgit_pageurl(cgit_query_repo, "diff", query),
-                      NULL, NULL);
-       if (info->status == DIFF_STATUS_COPIED ||
-           info->status == DIFF_STATUS_RENAMED) {
-               html_txt(info->new_path);
-               htmlf("</a> (%s from ", info->status == DIFF_STATUS_COPIED ?
-                     "copied" : "renamed");
-               query2 = fmt("id=%s", sha1_to_hex(info->old_sha1));
-               html_link_open(cgit_pageurl(cgit_query_repo, "view", query2),
-                              NULL, NULL);
-               html_txt(info->old_path);
-               html("</a>)");
-       } else {
-               html_txt(info->new_path);
-               html("</a>");
-       }
+       cgit_tree_link(info->new_path, NULL, NULL, cgit_query_head, curr_rev,
+                      info->new_path);
+       if (info->status == DIFF_STATUS_COPIED || info->status == DIFF_STATUS_RENAMED)
+               htmlf(" (%s from %s)",
+                     info->status == DIFF_STATUS_COPIED ? "copied" : "renamed",
+                     info->old_path);
        html("</td><td class='right'>");
        htmlf("%d", info->added + info->removed);
        html("</td><td class='graph'>");
@@ -145,16 +133,19 @@ void inspect_filepair(struct diff_filepair *pair)
 }
 
 
-void cgit_print_commit(const char *hex)
+void cgit_print_commit(char *hex)
 {
        struct commit *commit, *parent;
        struct commitinfo *info;
        struct commit_list *p;
        unsigned char sha1[20];
-       char *query;
-       char *filename;
+       char *tmp;
        int i;
 
+       if (!hex)
+               hex = cgit_query_head;
+       curr_rev = hex;
+
        if (get_sha1(hex, sha1)) {
                cgit_print_error(fmt("Bad object id: %s", hex));
                return;
@@ -181,11 +172,11 @@ void cgit_print_commit(const char *hex)
        html("</td><td class='right'>");
        cgit_print_date(info->committer_date, FMT_LONGDATE);
        html("</td></tr>\n");
-       html("<tr><th>tree</th><td colspan='2' class='sha1'><a href='");
-       query = fmt("h=%s&amp;id=%s", sha1_to_hex(commit->object.sha1),
-                   sha1_to_hex(commit->tree->object.sha1));
-       html_attr(cgit_pageurl(cgit_query_repo, "tree", query));
-       htmlf("'>%s</a></td></tr>\n", sha1_to_hex(commit->tree->object.sha1));
+       html("<tr><th>tree</th><td colspan='2' class='sha1'>");
+       tmp = xstrdup(hex);
+       cgit_tree_link(sha1_to_hex(commit->tree->object.sha1), NULL, NULL,
+                      cgit_query_head, tmp, NULL);
+       html("</td></tr>\n");
        for (p = commit->parents; p ; p = p->next) {
                parent = lookup_commit_reference(p->item->object.sha1);
                if (!parent) {
@@ -195,23 +186,19 @@ void cgit_print_commit(const char *hex)
                        continue;
                }
                html("<tr><th>parent</th>"
-                    "<td colspan='2' class='sha1'>"
-                    "<a href='");
-               query = fmt("h=%s", sha1_to_hex(p->item->object.sha1));
-               html_attr(cgit_pageurl(cgit_query_repo, "commit", query));
-               htmlf("'>%s</a> (<a href='",
-                     sha1_to_hex(p->item->object.sha1));
-               query = fmt("id=%s&amp;id2=%s", sha1_to_hex(parent->tree->object.sha1),
-                           sha1_to_hex(commit->tree->object.sha1));
-               html_attr(cgit_pageurl(cgit_query_repo, "diff", query));
-               html("'>diff</a>)</td></tr>");
+                    "<td colspan='2' class='sha1'>");
+               cgit_commit_link(sha1_to_hex(p->item->object.sha1), NULL, NULL,
+                                cgit_query_head, sha1_to_hex(p->item->object.sha1));
+               html(" (");
+               cgit_diff_link("diff", NULL, NULL, cgit_query_head, hex,
+                              sha1_to_hex(p->item->object.sha1), NULL);
+               html(")</td></tr>");
        }
        if (cgit_repo->snapshots) {
-               htmlf("<tr><th>download</th><td colspan='2' class='sha1'><a href='");
-               filename = fmt("%s-%s.zip", cgit_query_repo, hex);
-               html_attr(cgit_pageurl(cgit_query_repo, "snapshot",
-                                      fmt("id=%s&amp;name=%s", hex, filename)));
-               htmlf("'>%s</a></td></tr>", filename);
+               html("<tr><th>download</th><td colspan='2' class='sha1'>");
+               cgit_print_snapshot_links(cgit_query_repo, cgit_query_head,
+                                         hex, cgit_repo->snapshots);
+               html("</td></tr>");
        }
        html("</table>\n");
        html("<div class='commit-subject'>");
@@ -231,10 +218,9 @@ void cgit_print_commit(const char *hex)
                html("<div class='diffstat-summary'>");
                htmlf("%d files changed, %d insertions, %d deletions (",
                      files, total_adds, total_rems);
-               query = fmt("h=%s", hex);
-               html_link_open(cgit_pageurl(cgit_query_repo, "diff", query), NULL, NULL);
-               html("show diff</a>)");
-               html("</div>");
+               cgit_diff_link("show diff", NULL, NULL, cgit_query_head, hex,
+                              NULL, NULL);
+               html(")</div>");
        }
        cgit_free_commitinfo(info);
 }
index 4695e3a6442cb1b7bc1e2bbc75291e066d24a013..0be845fa41241656e936c2cab4704663c61fa66f 100644 (file)
--- a/ui-diff.c
+++ b/ui-diff.c
@@ -89,54 +89,52 @@ static void filepair_cb(struct diff_filepair *pair)
                cgit_print_error("Error running diff");
 }
 
-void cgit_print_diff(const char *head, const char *old_hex, const char *new_hex, char *path)
+void cgit_print_diff(const char *new_rev, const char *old_rev)
 {
        unsigned char sha1[20], sha2[20];
        enum object_type type;
        unsigned long size;
-       struct commit *commit;
-
-       if (head && !old_hex && !new_hex) {
-               get_sha1(head, sha1);
-               commit = lookup_commit_reference(sha1);
-               if (commit && !parse_commit(commit)) {
-                       html("<table class='diff'>");
-                       html("<tr><td>");
-                       cgit_diff_commit(commit, filepair_cb);
-                       html("</td></tr>");
-                       html("</table>");
-               }
+       struct commit *commit, *commit2;
+
+       if (!new_rev)
+               new_rev = cgit_query_head;
+       get_sha1(new_rev, sha1);
+       type = sha1_object_info(sha1, &size);
+       if (type == OBJ_BAD) {
+               cgit_print_error(fmt("Bad object name: %s", new_rev));
+               return;
+       }
+       if (type != OBJ_COMMIT) {
+               cgit_print_error(fmt("Unhandled object type: %s",
+                                    typename(type)));
                return;
        }
 
-       get_sha1(old_hex, sha1);
-       get_sha1(new_hex, sha2);
+       commit = lookup_commit_reference(sha1);
+       if (!commit || parse_commit(commit))
+               cgit_print_error(fmt("Bad commit: %s", sha1_to_hex(sha1)));
 
-       type = sha1_object_info(sha1, &size);
-       if (type == OBJ_BAD) {
+       if (old_rev)
+               get_sha1(old_rev, sha2);
+       else if (commit->parents && commit->parents->item)
+               hashcpy(sha2, commit->parents->item->object.sha1);
+       else
+               hashclr(sha2);
+
+       if (!is_null_sha1(sha2)) {
                type = sha1_object_info(sha2, &size);
                if (type == OBJ_BAD) {
-                       cgit_print_error(fmt("Bad object names: %s, %s", old_hex, new_hex));
+                       cgit_print_error(fmt("Bad object name: %s", sha1_to_hex(sha2)));
                        return;
                }
+               commit2 = lookup_commit_reference(sha2);
+               if (!commit2 || parse_commit(commit2))
+                       cgit_print_error(fmt("Bad commit: %s", sha1_to_hex(sha2)));
        }
 
        html("<table class='diff'>");
-       switch(type) {
-       case OBJ_BLOB:
-               html("<tr><td>");
-               header(sha1, path, 0644, sha2, path, 0644);
-               if (cgit_diff_files(sha1, sha2, print_line))
-                       cgit_print_error("Error running diff");
-               html("</td></tr>");
-               break;
-       case OBJ_TREE:
-               cgit_diff_tree(sha1, sha2, filepair_cb);
-               break;
-       default:
-               cgit_print_error(fmt("Unhandled object type: %s",
-                                    typename(type)));
-               break;
-       }
+       html("<tr><td>");
+       cgit_diff_tree(sha2, sha1, filepair_cb);
+       html("</td></tr>");
        html("</table>");
 }
index bb17e1dd98dff3c193fd3afdfd14737ef61cca2c..d38e40a17423cd507f63f3e4b0bfa5d77fb9c0f8 100644 (file)
--- a/ui-log.c
+++ b/ui-log.c
@@ -31,11 +31,8 @@ void print_commit(struct commit *commit)
        html("<tr><td>");
        cgit_print_age(commit->date, TM_WEEK * 2, FMT_SHORTDATE);
        html("</td><td>");
-       char *qry = fmt("h=%s", sha1_to_hex(commit->object.sha1));
-       char *url = cgit_pageurl(cgit_query_repo, "commit", qry);
-       html_link_open(url, NULL, NULL);
-       html_ntxt(cgit_max_msg_len, info->subject);
-       html_link_close();
+       cgit_commit_link(info->subject, NULL, NULL, cgit_query_head,
+                        sha1_to_hex(commit->object.sha1));
        if (cgit_repo->enable_log_filecount) {
                files = 0;
                lines = 0;
@@ -62,6 +59,9 @@ void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *path, i
        int argc = 2;
        int i;
 
+       if (!tip)
+               argv[1] = cgit_query_head;
+
        if (grep)
                argv[argc++] = fmt("--grep=%s", grep);
        if (path) {
@@ -113,17 +113,15 @@ void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *path, i
        if (pager) {
                html("<div class='pager'>");
                if (ofs > 0) {
-                       html("&nbsp;<a href='");
-                       html(cgit_pageurl(cgit_query_repo, cgit_query_page,
-                                         fmt("h=%s&amp;ofs=%d", tip, ofs-cnt)));
-                       html("'>[prev]</a>&nbsp;");
+                       cgit_log_link("[prev]", NULL, NULL, cgit_query_head,
+                                     cgit_query_sha1, cgit_query_path,
+                                     ofs - cnt);
+                       html("&nbsp;");
                }
-
                if ((commit = get_revision(&rev)) != NULL) {
-                       html("&nbsp;<a href='");
-                       html(cgit_pageurl(cgit_query_repo, "log",
-                                         fmt("h=%s&amp;ofs=%d", tip, ofs+cnt)));
-                       html("'>[next]</a>&nbsp;");
+                       cgit_log_link("[next]", NULL, NULL, cgit_query_head,
+                                     cgit_query_sha1, cgit_query_path,
+                                     ofs + cnt);
                }
                html("</div>");
        }
index e5c6c207818669ddb034bb2012ab6e44c6616498..4c86543673855e40adf43a33d32bfcf1297f899e 100644 (file)
@@ -44,16 +44,19 @@ static void print_modtime(struct repoinfo *repo)
 
 void cgit_print_repolist(struct cacheitem *item)
 {
-       struct repoinfo *repo;
-       int i;
+       int i, columns = 4;
        char *last_group = NULL;
 
+       if (cgit_enable_index_links)
+               columns++;
+
        cgit_print_docstart(cgit_root_title, item);
        cgit_print_pageheader(cgit_root_title, 0);
 
        html("<table class='list nowrap'>");
        if (cgit_index_header) {
-               html("<tr class='nohover'><td colspan='5' class='include-block'>");
+               htmlf("<tr class='nohover'><td colspan='%d' class='include-block'>",
+                     columns);
                html_include(cgit_index_header);
                html("</td></tr>");
        }
@@ -61,42 +64,45 @@ void cgit_print_repolist(struct cacheitem *item)
             "<th class='left'>Name</th>"
             "<th class='left'>Description</th>"
             "<th class='left'>Owner</th>"
-            "<th class='left'>Idle</th>"
-            "<th>Links</th></tr>\n");
+            "<th class='left'>Idle</th>");
+       if (cgit_enable_index_links)
+               html("<th>Links</th>");
+       html("</tr>\n");
 
        for (i=0; i<cgit_repolist.count; i++) {
-               repo = &cgit_repolist.repos[i];
-               if ((last_group == NULL && repo->group != NULL) ||
-                   (last_group != NULL && repo->group == NULL) ||
-                   (last_group != NULL && repo->group!= NULL &&
-                    strcmp(repo->group, last_group))) {
-                       html("<tr class='nohover'><td colspan='4' class='repogroup'>");
-                       html_txt(repo->group);
+               cgit_repo = &cgit_repolist.repos[i];
+               if ((last_group == NULL && cgit_repo->group != NULL) ||
+                   (last_group != NULL && cgit_repo->group == NULL) ||
+                   (last_group != NULL && cgit_repo->group != NULL &&
+                    strcmp(cgit_repo->group, last_group))) {
+                       htmlf("<tr class='nohover'><td colspan='%d' class='repogroup'>",
+                             columns);
+                       html_txt(cgit_repo->group);
                        html("</td></tr>");
-                       last_group = repo->group;
+                       last_group = cgit_repo->group;
                }
                htmlf("<tr><td class='%s'>",
-                     repo->group ? "sublevel-repo" : "toplevel-repo");
-               html_link_open(cgit_repourl(repo->url), repo->desc, NULL);
-               html_txt(repo->name);
+                     cgit_repo->group ? "sublevel-repo" : "toplevel-repo");
+               html_link_open(cgit_repourl(cgit_repo->url), NULL, NULL);
+               html_txt(cgit_repo->name);
                html_link_close();
                html("</td><td>");
-               html_ntxt(cgit_max_repodesc_len, repo->desc);
-               html("</td><td>");
-               html_txt(repo->owner);
+               html_ntxt(cgit_max_repodesc_len, cgit_repo->desc);
                html("</td><td>");
-               print_modtime(repo);
+               html_txt(cgit_repo->owner);
                html("</td><td>");
-               html_link_open(cgit_repourl(repo->url),
-                              "Summary", "button");
-               html("S</a>");
-               html_link_open(cgit_pageurl(repo->name, "log", NULL),
-                              "Log", "button");
-               html("L</a>");
-               html_link_open(cgit_pageurl(repo->name, "tree", NULL),
-                              "Files", "button");
-               html("F</a>");
-               html("</td></tr>\n");
+               print_modtime(cgit_repo);
+               html("</td>");
+               if (cgit_enable_index_links) {
+                       html("<td>");
+                       html_link_open(cgit_repourl(cgit_repo->url),
+                                      NULL, "button");
+                       html("summary</a>");
+                       cgit_log_link("log", NULL, "button", NULL, NULL, NULL, 0);
+                       cgit_tree_link("tree", NULL, "button", NULL, NULL, NULL);
+                       html("</td>");
+               }
+               html("</tr>\n");
        }
        html("</table>");
        cgit_print_docend();
index aba93e8c135860058a275e4aec8f85a9f796ecdc..5c5bcf35a71e8a09dfdc39f144af4b4e6629fd46 100644 (file)
@@ -57,13 +57,13 @@ char *cgit_repourl(const char *reponame)
        }
 }
 
-char *cgit_pageurl(const char *reponame, const char *pagename,
-                  const char *query)
+char *cgit_fileurl(const char *reponame, const char *pagename,
+                  const char *filename, const char *query)
 {
        if (cgit_virtual_root) {
                if (query)
-                       return fmt("%s/%s/%s/?%s", cgit_virtual_root, reponame,
-                                  pagename, query);
+                       return fmt("%s/%s/%s/%s?%s", cgit_virtual_root, reponame,
+                                  pagename, filename?filename:"", query);
                else
                        return fmt("%s/%s/%s/", cgit_virtual_root, reponame,
                                   pagename);
@@ -75,6 +75,37 @@ char *cgit_pageurl(const char *reponame, const char *pagename,
        }
 }
 
+char *cgit_pageurl(const char *reponame, const char *pagename,
+                  const char *query)
+{
+       return cgit_fileurl(reponame,pagename,0,query);
+}
+
+const char *cgit_repobasename(const char *reponame)
+{
+       /* I assume we don't need to store more than one repo basename */
+       static char rvbuf[1024];
+       int p;
+       const char *rv;
+       strncpy(rvbuf,reponame,sizeof(rvbuf));
+       if(rvbuf[sizeof(rvbuf)-1])
+               die("cgit_repobasename: truncated repository name '%s'", reponame);
+       p = strlen(rvbuf)-1;
+       /* strip trailing slashes */
+       while(p && rvbuf[p]=='/') rvbuf[p--]=0;
+       /* strip trailing .git */
+       if(p>=3 && !strncmp(&rvbuf[p-3],".git",4)) {
+               p -= 3; rvbuf[p--] = 0;
+       }
+       /* strip more trailing slashes if any */
+       while( p && rvbuf[p]=='/') rvbuf[p--]=0;
+       /* find last slash in the remaining string */
+       rv = strrchr(rvbuf,'/');
+       if(rv)
+               return ++rv;
+       return rvbuf;
+}
+
 char *cgit_currurl()
 {
        if (!cgit_virtual_root)
@@ -87,6 +118,166 @@ char *cgit_currurl()
                return fmt("%s/", cgit_virtual_root);
 }
 
+static char *repolink(char *title, char *class, char *page, char *head,
+                     char *path)
+{
+       char *delim = "?";
+
+       html("<a");
+       if (title) {
+               html(" title='");
+               html_attr(title);
+               html("'");
+       }
+       if (class) {
+               html(" class='");
+               html_attr(class);
+               html("'");
+       }
+       html(" href='");
+       if (cgit_virtual_root) {
+               html_attr(cgit_virtual_root);
+               if (cgit_virtual_root[strlen(cgit_virtual_root) - 1] != '/')
+                       html("/");
+               html_attr(cgit_repo->url);
+               if (cgit_repo->url[strlen(cgit_repo->url) - 1] != '/')
+                       html("/");
+               if (page) {
+                       html(page);
+                       html("/");
+                       if (path)
+                               html_attr(path);
+               }
+       } else {
+               html(cgit_script_name);
+               html("?url=");
+               html_attr(cgit_repo->url);
+               if (cgit_repo->url[strlen(cgit_repo->url) - 1] != '/')
+                       html("/");
+               if (page) {
+                       html(page);
+                       html("/");
+                       if (path)
+                               html_attr(path);
+               }
+               delim = "&amp;";
+       }
+       if (head && strcmp(head, cgit_repo->defbranch)) {
+               html(delim);
+               html("h=");
+               html_attr(head);
+               delim = "&amp;";
+       }
+       return fmt("%s", delim);
+}
+
+static void reporevlink(char *page, char *name, char *title, char *class,
+                       char *head, char *rev, char *path)
+{
+       char *delim;
+
+       delim = repolink(title, class, page, head, path);
+       if (rev && strcmp(rev, cgit_query_head)) {
+               html(delim);
+               html("id=");
+               html_attr(rev);
+       }
+       html("'>");
+       html_txt(name);
+       html("</a>");
+}
+
+void cgit_tree_link(char *name, char *title, char *class, char *head,
+                   char *rev, char *path)
+{
+       reporevlink("tree", name, title, class, head, rev, path);
+}
+
+void cgit_log_link(char *name, char *title, char *class, char *head,
+                  char *rev, char *path, int ofs)
+{
+       char *delim;
+
+       delim = repolink(title, class, "log", head, path);
+       if (rev && strcmp(rev, cgit_query_head)) {
+               html(delim);
+               html("id=");
+               html_attr(rev);
+               delim = "&";
+       }
+       if (ofs > 0) {
+               html(delim);
+               html("ofs=");
+               htmlf("%d", ofs);
+       }
+       html("'>");
+       html_txt(name);
+       html("</a>");
+}
+
+void cgit_commit_link(char *name, char *title, char *class, char *head,
+                     char *rev)
+{
+       if (strlen(name) > cgit_max_msg_len && cgit_max_msg_len >= 15) {
+               name[cgit_max_msg_len] = '\0';
+               name[cgit_max_msg_len - 1] = '.';
+               name[cgit_max_msg_len - 2] = '.';
+               name[cgit_max_msg_len - 3] = '.';
+       }
+       reporevlink("commit", name, title, class, head, rev, NULL);
+}
+
+void cgit_snapshot_link(char *name, char *title, char *class, char *head,
+                       char *rev, char *archivename)
+{
+       reporevlink("snapshot", name, title, class, head, rev, archivename);
+}
+
+void cgit_diff_link(char *name, char *title, char *class, char *head,
+                   char *new_rev, char *old_rev, char *path)
+{
+       char *delim;
+
+       delim = repolink(title, class, "diff", head, path);
+       if (new_rev && strcmp(new_rev, cgit_query_head)) {
+               html(delim);
+               html("id=");
+               html_attr(new_rev);
+               delim = "&amp;";
+       }
+       if (old_rev) {
+               html(delim);
+               html("id2=");
+               html_attr(old_rev);
+       }
+       html("'>");
+       html_txt(name);
+       html("</a>");
+}
+
+void cgit_object_link(struct object *obj)
+{
+       char *page, *arg, *url;
+
+       if (obj->type == OBJ_COMMIT) {
+                cgit_commit_link(fmt("commit %s", sha1_to_hex(obj->sha1)), NULL, NULL,
+                                cgit_query_head, sha1_to_hex(obj->sha1));
+               return;
+       } else if (obj->type == OBJ_TREE) {
+               page = "tree";
+               arg = "id";
+       } else {
+               page = "blob";
+               arg = "id";
+       }
+
+       url = cgit_pageurl(cgit_query_repo, page,
+                          fmt("%s=%s", arg, sha1_to_hex(obj->sha1)));
+       html_link_open(url, NULL, NULL);
+       htmlf("%s %s", typename(obj->type),
+             sha1_to_hex(obj->sha1));
+       html_link_close();
+}
 
 void cgit_print_date(time_t secs, char *format)
 {
@@ -152,7 +343,7 @@ void cgit_print_docstart(char *title, struct cacheitem *item)
        html("<title>");
        html_txt(title);
        html("</title>\n");
-       htmlf("<meta name='generator' content='cgit v%s'/>\n", cgit_version);
+       htmlf("<meta name='generator' content='cgit %s'/>\n", cgit_version);
        html("<link rel='stylesheet' type='text/css' href='");
        html_attr(cgit_css);
        html("'/>\n");
@@ -169,19 +360,38 @@ void cgit_print_docend()
 void cgit_print_pageheader(char *title, int show_search)
 {
        html("<table id='layout'>");
-       html("<tr><td id='header'>");
-       html(cgit_root_title);
-       html("</td><td id='logo'>");
+       html("<tr><td id='header'><a href='");
+       html_attr(cgit_rooturl());
+       html("'>");
+       html_txt(cgit_root_title);
+       html("</a></td><td id='logo'>");
        html("<a href='");
        html_attr(cgit_logo_link);
        htmlf("'><img src='%s' alt='logo'/></a>", cgit_logo);
        html("</td></tr>");
        html("<tr><td id='crumb'>");
-       htmlf("<a href='%s'>root</a>", cgit_rooturl());
        if (cgit_query_repo) {
-               htmlf(" : <a href='%s'>", cgit_repourl(cgit_repo->url));
                html_txt(cgit_repo->name);
-               htmlf("</a> : %s", title);
+               html(" (");
+               html_txt(cgit_query_head);
+               html(") : &nbsp;");
+               reporevlink(NULL, "summary", NULL, NULL, cgit_query_head,
+                           NULL, NULL);
+               html(" ");
+               cgit_log_link("log", NULL, NULL, cgit_query_head,
+                             cgit_query_sha1, cgit_query_path, 0);
+               html(" ");
+               cgit_tree_link("tree", NULL, NULL, cgit_query_head,
+                              cgit_query_sha1, NULL);
+               html(" ");
+               cgit_commit_link("commit", NULL, NULL, cgit_query_head,
+                             cgit_query_sha1);
+               html(" ");
+               cgit_diff_link("diff", NULL, NULL, cgit_query_head,
+                              cgit_query_sha1, cgit_query_sha2,
+                              cgit_query_path);
+       } else {
+               html_txt("Index of repositories");
        }
        html("</td>");
        html("<td id='search'>");
@@ -219,3 +429,5 @@ void cgit_print_snapshot_start(const char *mimetype, const char *filename,
                                         ttl_seconds(item->ttl)));
        html("\n");
 }
+
+/* vim:set sw=8: */
index 2257d6b9e574171c185e76a5fc21b7aeb7826882..bd34a28a41713cccffff7a52742e4a61038e0544 100644 (file)
 
 #include "cgit.h"
 
-static void cgit_print_zip(struct cacheitem *item, const char *hex, 
-                          const char *prefix, const char *filename)
+static int write_compressed_tar_archive(struct archiver_args *args,const char *filter)
 {
+       int rw[2];
+       pid_t gzpid;
+       int stdout2;
+       int status;
+       int rv;
+
+       stdout2 = chk_non_negative(dup(STDIN_FILENO), "Preserving STDOUT before compressing");
+       chk_zero(pipe(rw), "Opening pipe from compressor subprocess");
+       gzpid = chk_non_negative(fork(), "Forking compressor subprocess");
+       if(gzpid==0) {
+               /* child */
+               chk_zero(close(rw[1]), "Closing write end of pipe in child");
+               chk_zero(close(STDIN_FILENO), "Closing STDIN");
+               chk_non_negative(dup2(rw[0],STDIN_FILENO), "Redirecting compressor input to stdin");
+               execlp(filter,filter,NULL);
+               _exit(-1);
+       }
+       /* parent */
+       chk_zero(close(rw[0]), "Closing read end of pipe");
+       chk_non_negative(dup2(rw[1],STDOUT_FILENO), "Redirecting output to compressor");
+
+       rv = write_tar_archive(args);
+
+       chk_zero(close(STDOUT_FILENO), "Closing STDOUT redirected to compressor");
+       chk_non_negative(dup2(stdout2,STDOUT_FILENO), "Restoring uncompressed STDOUT");
+       chk_zero(close(stdout2), "Closing uncompressed STDOUT");
+       chk_zero(close(rw[1]), "Closing write end of pipe in parent");
+       chk_positive(waitpid(gzpid,&status,0), "Waiting on compressor process");
+       if(! ( WIFEXITED(status) && WEXITSTATUS(status)==0 ) )
+               cgit_print_error("Failed to compress archive");
+
+       return rv;
+}
+
+static int write_tar_gzip_archive(struct archiver_args *args)
+{
+       return write_compressed_tar_archive(args,"gzip");
+}
+
+static int write_tar_bzip2_archive(struct archiver_args *args)
+{
+       return write_compressed_tar_archive(args,"bzip2");
+}
+
+static const struct snapshot_archive_t {
+       const char *suffix;
+       const char *mimetype;
+       write_archive_fn_t write_func;
+       int bit;
+}      snapshot_archives[] = {
+       { ".zip", "application/x-zip", write_zip_archive, 0x1 },
+       { ".tar.gz", "application/x-tar", write_tar_gzip_archive, 0x2 },
+       { ".tar.bz2", "application/x-tar", write_tar_bzip2_archive, 0x4 },
+       { ".tar", "application/x-tar", write_tar_archive, 0x8 }
+};
+
+#define snapshot_archives_len (sizeof(snapshot_archives) / sizeof(*snapshot_archives))
+
+void cgit_print_snapshot(struct cacheitem *item, const char *head,
+                        const char *hex, const char *prefix,
+                        const char *filename, int snapshots)
+{
+       const struct snapshot_archive_t* sat;
        struct archiver_args args;
        struct commit *commit;
        unsigned char sha1[20];
+       int f, sl, fnl = strlen(filename);
 
-       if (get_sha1(hex, sha1)) {
-               cgit_print_error(fmt("Bad object id: %s", hex));
+       for(f=0; f<snapshot_archives_len; f++) {
+               sat = &snapshot_archives[f];
+               if(!(snapshots & sat->bit))
+                       continue;
+               sl = strlen(sat->suffix);
+               if(fnl<sl || strcmp(&filename[fnl-sl],sat->suffix))
+                       continue;
+               if (!hex)
+                       hex = head;
+               if(get_sha1(hex, sha1)) {
+                       cgit_print_error(fmt("Bad object id: %s", hex));
+                       return;
+               }
+               commit = lookup_commit_reference(sha1);
+               if(!commit) {
+                       cgit_print_error(fmt("Not a commit reference: %s", hex));
+                       return;;
+               }
+               memset(&args,0,sizeof(args));
+               args.base = fmt("%s/", prefix);
+               args.tree = commit->tree;
+               cgit_print_snapshot_start(sat->mimetype, filename, item);
+               (*sat->write_func)(&args);
                return;
        }
-       commit = lookup_commit_reference(sha1);
+       cgit_print_error(fmt("Unsupported snapshot format: %s", filename));
+}
 
-       if (!commit) {
-               cgit_print_error(fmt("Not a commit reference: %s", hex));
-               return;
-       }
+void cgit_print_snapshot_links(const char *repo, const char *head,
+                              const char *hex, int snapshots)
+{
+       const struct snapshot_archive_t* sat;
+       char *filename;
+       int f;
 
-       memset(&args, 0, sizeof(args));
-       args.base = fmt("%s/", prefix);
-       args.tree = commit->tree;
-       
-       cgit_print_snapshot_start("application/x-zip", filename, item);
-       write_zip_archive(&args);
+       for(f=0; f<snapshot_archives_len; f++) {
+               sat = &snapshot_archives[f];
+               if(!(snapshots & sat->bit))
+                       continue;
+               filename = fmt("%s-%s%s", cgit_repobasename(repo), hex,
+                              sat->suffix);
+               cgit_snapshot_link(filename, NULL, NULL, (char *)head,
+                                  (char *)hex, filename);
+               html("<br/>");
+       }
 }
 
-
-void cgit_print_snapshot(struct cacheitem *item, const char *hex, 
-                        const char *format, const char *prefix,
-                        const char *filename)
+int cgit_parse_snapshots_mask(const char *str)
 {
-       if (!strcmp(format, "zip"))
-               cgit_print_zip(item, hex, prefix, filename);
-       else
-               cgit_print_error(fmt("Unsupported snapshot format: %s", 
-                                    format));
+       const struct snapshot_archive_t* sat;
+       static const char *delim = " \t,:/|;";
+       int f, tl, rv = 0;
+
+       /* favor legacy setting */
+       if(atoi(str))
+               return 1;
+       for(;;) {
+               str += strspn(str,delim);
+               tl = strcspn(str,delim);
+               if(!tl)
+                       break;
+               for(f=0; f<snapshot_archives_len; f++) {
+                       sat = &snapshot_archives[f];
+                       if(!(strncmp(sat->suffix, str, tl) &&
+                            strncmp(sat->suffix+1, str, tl-1))) {
+                               rv |= sat->bit;
+                               break;
+                       }
+               }
+               str += tl;
+       }
+       return rv;
 }
+
+/* vim:set sw=8: */
index 4bda4c2f73f8ed98195e5d0c427c6d85a3a9cb05..de8a1808cf8a3f6e498b3ad8e06148768bdb4bb7 100644 (file)
@@ -15,8 +15,10 @@ static int cgit_print_branch_cb(const char *refname, const unsigned char *sha1,
 {
        struct commit *commit;
        struct commitinfo *info;
-       char buf[256], *url;
+       char buf[256];
+       char *ref;
 
+       ref = xstrdup(refname);
        strncpy(buf, refname, sizeof(buf));
        commit = lookup_commit(sha1);
        // object is not really parsed at this point, because of some fallout
@@ -25,21 +27,13 @@ static int cgit_print_branch_cb(const char *refname, const unsigned char *sha1,
        if (commit && !parse_commit(commit)){
                info = cgit_parse_commit(commit);
                html("<tr><td>");
-               url = cgit_pageurl(cgit_query_repo, "log",
-                                  fmt("h=%s", refname));
-               html_link_open(url, NULL, NULL);
-               html_txt(buf);
-               html_link_close();
+               cgit_log_link(ref, NULL, NULL, ref, NULL, NULL, 0);
                html("</td><td>");
                cgit_print_age(commit->date, -1, NULL);
                html("</td><td>");
                html_txt(info->author);
                html("</td><td>");
-               url = cgit_pageurl(cgit_query_repo, "commit",
-                                  fmt("h=%s", sha1_to_hex(sha1)));
-               html_link_open(url, NULL, NULL);
-               html_ntxt(cgit_max_msg_len, info->subject);
-               html_link_close();
+               cgit_commit_link(info->subject, NULL, NULL, ref, NULL);
                html("</td></tr>\n");
                cgit_free_commitinfo(info);
        } else {
@@ -49,33 +43,10 @@ static int cgit_print_branch_cb(const char *refname, const unsigned char *sha1,
                htmlf("*** bad ref %s ***", sha1_to_hex(sha1));
                html("</td></tr>\n");
        }
+       free(ref);
        return 0;
 }
 
-
-static void cgit_print_object_ref(struct object *obj)
-{
-       char *page, *arg, *url;
-
-       if (obj->type == OBJ_COMMIT) {
-               page = "commit";
-               arg = "h";
-       } else if (obj->type == OBJ_TREE) {
-               page = "tree";
-               arg = "id";
-       } else {
-               page = "view";
-               arg = "id";
-       }
-
-       url = cgit_pageurl(cgit_query_repo, page,
-                          fmt("%s=%s", arg, sha1_to_hex(obj->sha1)));
-       html_link_open(url, NULL, NULL);
-       htmlf("%s %s", typename(obj->type),
-             sha1_to_hex(obj->sha1));
-       html_link_close();
-}
-
 static void print_tag_header()
 {
        html("<tr class='nohover'><th class='left'>Tag</th>"
@@ -104,8 +75,8 @@ static int cgit_print_tag_cb(const char *refname, const unsigned char *sha1,
                if (!header)
                        print_tag_header();
                html("<tr><td>");
-               url = cgit_pageurl(cgit_query_repo, "view",
-                                  fmt("id=%s", sha1_to_hex(sha1)));
+               url = cgit_pageurl(cgit_query_repo, "tag",
+                                  fmt("id=%s", refname));
                html_link_open(url, NULL, NULL);
                html_txt(buf);
                html_link_close();
@@ -116,7 +87,7 @@ static int cgit_print_tag_cb(const char *refname, const unsigned char *sha1,
                if (info->tagger)
                        html(info->tagger);
                html("</td><td>");
-               cgit_print_object_ref(tag->tagged);
+               cgit_object_link(tag->tagged);
                html("</td></tr>\n");
        } else {
                if (!header)
@@ -124,7 +95,7 @@ static int cgit_print_tag_cb(const char *refname, const unsigned char *sha1,
                html("<tr><td>");
                html_txt(buf);
                html("</td><td colspan='2'/><td>");
-               cgit_print_object_ref(obj);
+               cgit_object_link(obj);
                html("</td></tr>\n");
        }
        return 0;
diff --git a/ui-tag.c b/ui-tag.c
new file mode 100644 (file)
index 0000000..6d761f3
--- /dev/null
+++ b/ui-tag.c
@@ -0,0 +1,74 @@
+/* ui-tag.c: display a tag
+ *
+ * Copyright (C) 2007 Lars Hjemli
+ *
+ * Licensed under GNU General Public License v2
+ *   (see COPYING for full license text)
+ */
+
+#include "cgit.h"
+
+
+static void print_tag_content(char *buf)
+{
+       char *p;
+
+       if (!buf)
+               return;
+
+       html("<div class='commit-subject'>");
+       p = strchr(buf, '\n');
+       if (p)
+               *p = '\0';
+       html_txt(buf);
+       html("</div>");
+       if (p) {
+               html("<div class='commit-msg'>");
+               html_txt(++p);
+               html("</div>");
+       }
+}
+
+void cgit_print_tag(char *revname)
+{
+       unsigned char sha1[20];
+       struct object *obj;
+       struct tag *tag;
+       struct taginfo *info;
+
+       if (get_sha1(revname, sha1)) {
+               cgit_print_error(fmt("Bad tag reference: %s", revname));
+               return;
+       }
+       obj = parse_object(sha1);
+       if (!obj) {
+               cgit_print_error(fmt("Bad object id: %s", sha1_to_hex(sha1)));
+               return;
+       }
+       if (obj->type == OBJ_TAG) {
+               tag = lookup_tag(sha1);
+               if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag))) {
+                       cgit_print_error(fmt("Bad tag object: %s", revname));
+                       return;
+               }
+               html("<table class='commit-info'>\n");
+               htmlf("<tr><td>Tag name</td><td>%s (%s)</td></tr>\n",
+                     revname, sha1_to_hex(sha1));
+               if (info->tagger_date > 0) {
+                       html("<tr><td>Tag date</td><td>");
+                       cgit_print_date(info->tagger_date, FMT_LONGDATE);
+                       html("</td></tr>\n");
+               }
+               if (info->tagger) {
+                       html("<tr><td>Tagged by</td><td>");
+                       html_txt(info->tagger);
+                       html("</td></tr>\n");
+               }
+               html("<tr><td>Tagged object</td><td>");
+               cgit_object_link(tag->tagged);
+               html("</td></tr>\n");
+               html("</table>\n");
+               print_tag_content(info->msg);
+       }
+       return;
+}
index 21dd533cf8edfb9432cd8a88d9f5d762340e7462..1cb09f717a56826dfd2cb132cdd78f72c3aad341 100644 (file)
--- a/ui-tree.c
+++ b/ui-tree.c
@@ -9,16 +9,64 @@
 #include "cgit.h"
 
 char *curr_rev;
+char *match_path;
+int header = 0;
 
-static int print_entry(const unsigned char *sha1, const char *base,
-                      int baselen, const char *pathname, unsigned int mode,
-                      int stage)
+static void print_object(const unsigned char *sha1, char *path)
+{
+       enum object_type type;
+       unsigned char *buf;
+       unsigned long size, lineno, start, idx;
+
+       type = sha1_object_info(sha1, &size);
+       if (type == OBJ_BAD) {
+               cgit_print_error(fmt("Bad object name: %s",
+                                    sha1_to_hex(sha1)));
+               return;
+       }
+
+       buf = read_sha1_file(sha1, &type, &size);
+       if (!buf) {
+               cgit_print_error(fmt("Error reading object %s",
+                                    sha1_to_hex(sha1)));
+               return;
+       }
+
+       html(" blob: <a href='");
+       html_attr(cgit_pageurl(cgit_query_repo, "blob", fmt("id=%s", sha1_to_hex(sha1))));
+       htmlf("'>%s</a>",sha1_to_hex(sha1));
+
+       html("<table class='blob'>\n");
+       idx = 0;
+       start = 0;
+       lineno = 0;
+       while(idx < size) {
+               if (buf[idx] == '\n') {
+                       buf[idx] = '\0';
+                       htmlf("<tr><td class='no'><a name='%d'>%1$d</a></td><td class='txt'>",
+                             ++lineno);
+                       html_txt(buf + start);
+                       html("</td></tr>\n");
+                       start = idx + 1;
+               }
+               idx++;
+       }
+       html("</table>\n");
+}
+
+
+static int ls_item(const unsigned char *sha1, const char *base, int baselen,
+                  const char *pathname, unsigned int mode, int stage)
 {
        char *name;
+       char *fullpath;
        enum object_type type;
        unsigned long size = 0;
 
        name = xstrdup(pathname);
+       fullpath = fmt("%s%s%s", cgit_query_path ? cgit_query_path : "",
+                      cgit_query_path ? "/" : "", name);
+
        type = sha1_object_info(sha1, &size);
        if (type == OBJ_BAD && !S_ISGITLINK(mode)) {
                htmlf("<tr><td colspan='3'>Bad object: %s %s</td></tr>",
@@ -26,79 +74,140 @@ static int print_entry(const unsigned char *sha1, const char *base,
                      sha1_to_hex(sha1));
                return 0;
        }
-       html("<tr><td class='filemode'>");
+
+       html("<tr><td class='ls-mode'>");
        html_filemode(mode);
-       html("</td><td ");
+       html("</td><td>");
        if (S_ISGITLINK(mode)) {
-               htmlf("class='ls-mod'><a href='");
+               htmlf("<a class='ls-mod' href='");
                html_attr(fmt(cgit_repo->module_link,
                              name,
                              sha1_to_hex(sha1)));
+               html("'>");
+               html_txt(name);
+               html("</a>");
        } else if (S_ISDIR(mode)) {
-               html("class='ls-dir'><a href='");
-               html_attr(cgit_pageurl(cgit_query_repo, "tree",
-                                      fmt("h=%s&amp;id=%s&amp;path=%s%s/",
-                                          curr_rev,
-                                          sha1_to_hex(sha1),
-                                          cgit_query_path ? cgit_query_path : "",
-                                          pathname)));
+               cgit_tree_link(name, NULL, "ls-dir", cgit_query_head,
+                              curr_rev, fullpath);
        } else {
-               html("class='ls-blob'><a href='");
-               html_attr(cgit_pageurl(cgit_query_repo, "view",
-                                     fmt("h=%s&amp;id=%s&amp;path=%s%s", curr_rev,
-                                         sha1_to_hex(sha1),
-                                         cgit_query_path ? cgit_query_path : "",
-                                         pathname)));
+               cgit_tree_link(name, NULL, "ls-blob", cgit_query_head,
+                              curr_rev, fullpath);
        }
-       htmlf("'>%s</a></td>", name);
-       htmlf("<td class='filesize'>%li</td>", size);
-
-       html("<td class='links'><a href='");
-       html_attr(cgit_pageurl(cgit_query_repo, "log",
-                              fmt("h=%s&amp;path=%s%s",
-                                  curr_rev,
-                                  cgit_query_path ? cgit_query_path : "",
-                                  pathname)));
-       html("'>history</a></td>");
-       html("</tr>\n");
+       htmlf("</td><td class='ls-size'>%li</td>", size);
+
+       html("<td>");
+       cgit_log_link("log", NULL, "button", cgit_query_head, curr_rev,
+                     fullpath, 0);
+       html("</td></tr>\n");
        free(name);
        return 0;
 }
 
-void cgit_print_tree(const char *rev, const char *hex, char *path)
+static void ls_head()
+{
+       html("<table class='list'>\n");
+       html("<tr class='nohover'>");
+       html("<th class='left'>Mode</th>");
+       html("<th class='left'>Name</th>");
+       html("<th class='right'>Size</th>");
+       html("<th/>");
+       html("</tr>\n");
+       header = 1;
+}
+
+static void ls_tail()
+{
+       if (!header)
+               return;
+       html("</table>\n");
+       header = 0;
+}
+
+static void ls_tree(const unsigned char *sha1, char *path)
 {
        struct tree *tree;
+
+       tree = parse_tree_indirect(sha1);
+       if (!tree) {
+               cgit_print_error(fmt("Not a tree object: %s",
+                                    sha1_to_hex(sha1)));
+               return;
+       }
+
+       ls_head();
+       read_tree_recursive(tree, "", 0, 1, NULL, ls_item);
+       ls_tail();
+}
+
+
+static int walk_tree(const unsigned char *sha1, const char *base, int baselen,
+                    const char *pathname, unsigned mode, int stage)
+{
+       static int state;
+       static char buffer[PATH_MAX];
+       char *url;
+
+       if (state == 0) {
+               memcpy(buffer, base, baselen);
+               strcpy(buffer+baselen, pathname);
+               url = cgit_pageurl(cgit_query_repo, "tree",
+                                  fmt("h=%s&amp;path=%s", curr_rev, buffer));
+               html("/");
+               cgit_tree_link(xstrdup(pathname), NULL, NULL, cgit_query_head,
+                              curr_rev, buffer);
+
+               if (strcmp(match_path, buffer))
+                       return READ_TREE_RECURSIVE;
+
+               if (S_ISDIR(mode)) {
+                       state = 1;
+                       ls_head();
+                       return READ_TREE_RECURSIVE;
+               } else {
+                       print_object(sha1, buffer);
+                       return 0;
+               }
+       }
+       ls_item(sha1, base, baselen, pathname, mode, stage);
+       return 0;
+}
+
+
+/*
+ * Show a tree or a blob
+ *   rev:  the commit pointing at the root tree object
+ *   path: path to tree or blob
+ */
+void cgit_print_tree(const char *rev, char *path)
+{
        unsigned char sha1[20];
        struct commit *commit;
+       const char *paths[] = {path, NULL};
+
+       if (!rev)
+               rev = cgit_query_head;
 
        curr_rev = xstrdup(rev);
-       get_sha1(rev, sha1);
+       if (get_sha1(rev, sha1)) {
+               cgit_print_error(fmt("Invalid revision name: %s", rev));
+               return;
+       }
        commit = lookup_commit_reference(sha1);
        if (!commit || parse_commit(commit)) {
-               cgit_print_error(fmt("Invalid head: %s", rev));
+               cgit_print_error(fmt("Invalid commit reference: %s", rev));
                return;
        }
-       if (!hex)
-               hex = sha1_to_hex(commit->tree->object.sha1);
 
-       if (get_sha1_hex(hex, sha1)) {
-               cgit_print_error(fmt("Invalid object id: %s", hex));
-               return;
-       }
-       tree = parse_tree_indirect(sha1);
-       if (!tree) {
-               cgit_print_error(fmt("Not a tree object: %s", hex));
+       html("path: <a href='");
+       html_attr(cgit_pageurl(cgit_query_repo, "tree", fmt("h=%s", rev)));
+       html("'>root</a>");
+
+       if (path == NULL) {
+               ls_tree(commit->tree->object.sha1, NULL);
                return;
        }
 
-       html_txt(path);
-       html("<table class='list'>\n");
-       html("<tr class='nohover'>");
-       html("<th class='left'>Mode</th>");
-       html("<th class='left'>Name</th>");
-       html("<th class='right'>Size</th>");
-       html("<th/>");
-       html("</tr>\n");
-       read_tree_recursive(tree, "", 0, 1, NULL, print_entry);
-       html("</table>\n");
+       match_path = path;
+       read_tree_recursive(commit->tree, NULL, 0, 0, paths, walk_tree);
+       ls_tail();
 }
diff --git a/ui-view.c b/ui-view.c
deleted file mode 100644 (file)
index 8873415..0000000
--- a/ui-view.c
+++ /dev/null
@@ -1,55 +0,0 @@
-/* ui-view.c: functions to output _any_ object, given it's sha1
- *
- * Copyright (C) 2006 Lars Hjemli
- *
- * Licensed under GNU General Public License v2
- *   (see COPYING for full license text)
- */
-
-#include "cgit.h"
-
-void cgit_print_view(const char *hex, char *path)
-{
-       unsigned char sha1[20];
-       enum object_type type;
-       unsigned char *buf;
-       unsigned long size;
-
-       if (get_sha1_hex(hex, sha1)){
-               cgit_print_error(fmt("Bad hex value: %s", hex));
-               return;
-       }
-
-       type = sha1_object_info(sha1, &size);
-       if (type == OBJ_BAD) {
-               cgit_print_error(fmt("Bad object name: %s", hex));
-               return;
-       }
-
-       buf = read_sha1_file(sha1, &type, &size);
-       if (!buf) {
-               cgit_print_error(fmt("Error reading object %s", hex));
-               return;
-       }
-
-       buf[size] = '\0';
-       html("<table class='list'>\n");
-       html("<tr class='nohover'><th class='left'>");
-       if (path)
-               htmlf("%s (", path);
-       htmlf("%s %s, %li bytes", typename(type), hex, size);
-       if (path)
-               html(")");
-
-       html(" <a href='");
-       html_attr(cgit_pageurl(cgit_query_repo, "blob", 
-                              fmt("id=%s&amp;path=%s", 
-                                  hex,
-                                  path)));
-       html("'>download</a>");
-       html("</th></tr>\n");
-       html("<tr><td class='blob'>\n");
-       html_txt(buf);
-       html("\n</td></tr>\n");
-       html("</table>\n");
-}