aboutsummaryrefslogtreecommitdiff
diff refs
from: back
to: back
| flip
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--.gitmodules8
-rw-r--r--Makefile102
-rw-r--r--cgit.c36
-rw-r--r--cgit.css75
-rw-r--r--cgit.h47
-rw-r--r--cgitrc9
-rwxr-xr-xgen-version.sh20
m---------git0
-rw-r--r--parsing.c2
-rw-r--r--shared.c45
-rwxr-xr-xsubmodules.sh181
-rw-r--r--ui-commit.c78
-rw-r--r--ui-diff.c64
-rw-r--r--ui-log.c26
-rw-r--r--ui-repolist.c66
-rw-r--r--ui-shared.c234
-rw-r--r--ui-snapshot.c156
-rw-r--r--ui-summary.c49
-rw-r--r--ui-tag.c74
-rw-r--r--ui-tree.c211
-rw-r--r--ui-view.c55
22 files changed, 612 insertions, 927 deletions
diff --git a/.gitignore b/.gitignore
index 5664962..c4c9ac3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,4 @@
# Files I don't care to see in git-status/commit
cgit
-VERSION
*.o
*~
diff --git a/.gitmodules b/.gitmodules
index 1daea94..51dd1ef 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,5 @@
-[submodule "git"]
- url = git://git.kernel.org/pub/scm/git/git.git
- path = git
+# 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
diff --git a/Makefile b/Makefile
index fcbe3e4..57f80f8 100644
--- a/Makefile
+++ b/Makefile
@@ -1,71 +1,87 @@
-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
+CGIT_VERSION = 0.5
+
+prefix = /var/www/htdocs/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
+CACHE_ROOT = /var/cache/cgit
+CGIT_CONFIG = /etc/cgitrc
+CGIT_SCRIPT_NAME = cgit.cgi
#
# 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-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
+ ui-summary.o ui-log.o ui-view.o ui-tree.o ui-commit.o ui-diff.o \
+ ui-snapshot.o ui-blob.o
-all: cgit git
+CFLAGS += -Wall
-VERSION: force-version
- @./gen-version.sh "$(CGIT_VERSION)"
--include VERSION
+ifdef DEBUG
+ CFLAGS += -g
+endif
-
-CFLAGS += -g -Wall -Igit
+CFLAGS += -Igit
CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER)'
CFLAGS += -DCGIT_VERSION='"$(CGIT_VERSION)"'
CFLAGS += -DCGIT_CONFIG='"$(CGIT_CONFIG)"'
CFLAGS += -DCGIT_SCRIPT_NAME='"$(CGIT_SCRIPT_NAME)"'
-cgit: cgit.c $(OBJECTS)
- $(CC) $(CFLAGS) cgit.c -o cgit $(OBJECTS) $(EXTLIBS)
+#
+# 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
-$(OBJECTS): cgit.h git/xdiff/lib.a git/libgit.a VERSION
-git/xdiff/lib.a: | git
+#
+# basic build rules
+#
+all: cgit
-git/libgit.a: | git
+cgit: cgit.c cgit.h $(OBJECTS)
+ $(CC) $(CFLAGS) cgit.c -o cgit $(OBJECTS) $(EXTLIBS)
-git:
- cd git && $(MAKE) xdiff/lib.a
- cd git && $(MAKE) libgit.a
+$(OBJECTS): cgit.h git/libgit.a
-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)/*
+git/libgit.a:
+ $(INITGIT)
+ $(MAKE) -C git
-uninstall:
- rm -f $(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME)
- rm -f $(CGIT_SCRIPT_PATH)/cgit.css
- rm -rf $(CACHE_ROOT)
+#
+# phony targets
+#
+install: all clean-cache
+ mkdir -p $(prefix)
+ install cgit $(prefix)/$(CGIT_SCRIPT_NAME)
+ install cgit.css $(prefix)/cgit.css
-clean:
- rm -f cgit VERSION *.o
- cd git && $(MAKE) clean
+clean-cgit:
+ rm -f cgit *.o
-distclean: clean
+distclean-cgit: clean-cgit
git clean -d -x
- cd git && git clean -d -x
-get-git:
- curl $(GIT_URL) | tar -xj && rm -rf git && mv git-$(GIT_VER) git
+clean-sub:
+ $(MAKE) -C git clean
+
+distclean-sub: clean-sub
+ $(shell cd git && git clean -d -x)
+
+clean-cache:
+ rm -rf $(CACHE_ROOT)/*
+
+clean: clean-cgit clean-sub
+
+distclean: distclean-cgit distclean-sub
+
+.PHONY: all install clean clean-cgit clean-sub clean-cache \
+ distclean distclean-cgit distclean-sub
diff --git a/cgit.c b/cgit.c
index c86d290..34e590e 100644
--- a/cgit.c
+++ b/cgit.c
@@ -8,6 +8,9 @@
#include "cgit.h"
+const char cgit_version[] = CGIT_VERSION;
+
+
static int cgit_prepare_cache(struct cacheitem *item)
{
if (!cgit_repo && cgit_query_repo) {
@@ -26,15 +29,13 @@ static int cgit_prepare_cache(struct cacheitem *item)
}
if (!cgit_cmd) {
- item->name = xstrdup(fmt("%s/%s/index.%s.html", cgit_cache_root,
- cache_safe_filename(cgit_repo->url),
- cache_safe_filename(cgit_querystring)));
+ item->name = xstrdup(fmt("%s/%s/index.html", cgit_cache_root,
+ cache_safe_filename(cgit_repo->url)));
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)
@@ -68,10 +69,8 @@ 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_head, cgit_query_sha1,
- cgit_repobasename(cgit_repo->url),
- cgit_query_path,
- cgit_repo->snapshots );
+ cgit_print_snapshot(item, cgit_query_sha1, "zip",
+ cgit_repo->url, cgit_query_name);
return;
}
@@ -93,21 +92,22 @@ static void cgit_print_repo_page(struct cacheitem *item)
switch(cgit_cmd) {
case CMD_LOG:
- cgit_print_log(cgit_query_sha1, cgit_query_ofs,
+ cgit_print_log(cgit_query_head, cgit_query_ofs,
cgit_max_commit_count, cgit_query_search,
cgit_query_path, 1);
break;
case CMD_TREE:
- cgit_print_tree(cgit_query_sha1, cgit_query_path);
+ cgit_print_tree(cgit_query_head, cgit_query_sha1, cgit_query_path);
break;
case CMD_COMMIT:
- cgit_print_commit(cgit_query_sha1);
+ cgit_print_commit(cgit_query_head);
break;
- case CMD_TAG:
- cgit_print_tag(cgit_query_sha1);
+ case CMD_VIEW:
+ cgit_print_view(cgit_query_sha1, cgit_query_path);
break;
case CMD_DIFF:
- cgit_print_diff(cgit_query_sha1, cgit_query_sha2);
+ cgit_print_diff(cgit_query_head, cgit_query_sha1, cgit_query_sha2,
+ cgit_query_path);
break;
default:
cgit_print_error("Invalid request");
@@ -227,7 +227,6 @@ 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);
@@ -235,8 +234,7 @@ int main(int argc, const char **argv)
cgit_repolist.count = 0;
cgit_repolist.repos = NULL;
- cgit_read_config(cgit_config_env ? cgit_config_env : CGIT_CONFIG,
- cgit_global_config_cb);
+ cgit_read_config(CGIT_CONFIG, cgit_global_config_cb);
cgit_repo = NULL;
if (getenv("SCRIPT_NAME"))
cgit_script_name = xstrdup(getenv("SCRIPT_NAME"));
diff --git a/cgit.css b/cgit.css
index 54bbfcc..8977533 100644
--- a/cgit.css
+++ b/cgit.css
@@ -1,7 +1,6 @@
body {
- font-family: arial, sans-serif;
+ font-family: arial;
font-size: 11pt;
- color: black;
background: white;
}
@@ -95,14 +94,6 @@ 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;
@@ -123,19 +114,15 @@ td#crumb {
td#crumb a {
color: #ccc;
- background-color: #666;
- padding: 0em 0.5em 0em 0.5em;
}
td#crumb a:hover {
- color: #666;
- background-color: #ccc;
- text-decoration: none;
+ color: #eee;
}
td#search {
text-align: right;
- vertical-align: middle;
+ vertical-align: center;
padding-right: 0.5em;
}
@@ -184,47 +171,35 @@ div.error {
margin: 1em 2em;
}
-a.ls-blob, a.ls-dir, a.ls-mod {
+td.ls-blob, td.ls-dir, td.ls-mod {
font-family: monospace;
}
-td.ls-size {
- text-align: right;
+div.ls-dir a {
+ font-weight: bold;
}
-td.ls-size {
- font-family: monospace;
+th.filesize, td.filesize {
+ text-align: right;
}
-td.ls-mode {
+td.filesize {
font-family: monospace;
}
-table.blob {
- margin-top: 0.5em;
- border-top: solid 1px black;
-}
-
-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.links {
+ font-size: 80%;
+ padding-left: 2em;
}
-table.blob td.no a:hover {
- color: black;
- text-decoration: none;
+td.filemode {
+ font-family: monospace;
}
-table.blob td.txt {
+td.blob {
white-space: pre;
font-family: monospace;
- padding-left: 0.5em;
+ background-color: white;
}
table.nowrap td {
@@ -240,7 +215,6 @@ 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 {
@@ -313,7 +287,7 @@ table.diffstat td.upd a {
table.diffstat td.graph {
width: 75%;
- vertical-align: middle;
+ vertical-align: center;
}
table.diffstat td.graph table {
@@ -334,6 +308,10 @@ 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;
@@ -362,7 +340,7 @@ table.diff td div.del {
}
.sha1 {
- font-family: monospace;
+ font-family: courier;
font-size: 90%;
}
@@ -381,17 +359,16 @@ table.list td.repogroup {
a.button {
font-size: 80%;
- color: #aaa;
- background-color: #eee;
- border: solid 1px #aaa;
+ color: #333;
+ background-color: #ccc;
+ border: solid 1px #999;
padding: 0em 0.5em;
margin: 0.1em 0.25em;
}
a.button:hover {
text-decoration: none;
- color: #333;
- background-color: #ccc;
+ background-color: #eee;
}
a.primary {
diff --git a/cgit.h b/cgit.h
index e3d9cb8..2f3fca1 100644
--- a/cgit.h
+++ b/cgit.h
@@ -25,9 +25,10 @@
#define CMD_COMMIT 2
#define CMD_DIFF 3
#define CMD_TREE 4
-#define CMD_BLOB 5
-#define CMD_SNAPSHOT 6
-#define CMD_TAG 7
+#define CMD_VIEW 5
+#define CMD_BLOB 6
+#define CMD_SNAPSHOT 7
+
/*
* Dateformats used on misc. pages
@@ -98,7 +99,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;
@@ -118,7 +119,6 @@ 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,10 +157,8 @@ 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);
@@ -201,26 +199,9 @@ 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);
@@ -234,16 +215,14 @@ 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, 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);
+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);
#endif /* CGIT_H */
diff --git a/cgitrc b/cgitrc
index 1040997..0f602e4 100644
--- a/cgitrc
+++ b/cgitrc
@@ -8,15 +8,10 @@
#nocache=0
-## Set allowed snapshot types by default. Can be overridden per repo
-# can be any combination of zip/tar.gz/tar.bz2/tar
+## Enable/disable snapshots by default. This can be overridden per repo
#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
@@ -114,7 +109,7 @@
#repo.desc=the caching cgi for git
#repo.path=/pub/git/cgit
#repo.owner=Lars Hjemli
-#repo.snapshots=tar.bz2 # override a sitewide snapshot-setting
+#repo.snapshots=1 # 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
deleted file mode 100755
index 739c83e..0000000
--- a/gen-version.sh
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/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
-Subproject 86bab9615c3516d4ac7756ae3c1285d331b78f0
+Subproject aba170cdb4874b72dd619e6f7bbc13c33295f83
diff --git a/parsing.c b/parsing.c
index 2c05c09..74a2484 100644
--- 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 = trim_end(p + 1, '/');
+ cgit_query_path = xstrdup(p + 1);
}
cgit_cmd = cgit_get_cmd_index(cmd + 1);
cgit_query_page = xstrdup(cmd + 1);
diff --git a/shared.c b/shared.c
index 077934f..b6d2fa1 100644
--- a/shared.c
+++ b/shared.c
@@ -12,8 +12,6 @@ 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";
@@ -28,7 +26,6 @@ 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;
@@ -62,8 +59,7 @@ int htmlfd = 0;
int cgit_get_cmd_index(const char *cmd)
{
- static char *cmds[] = {"log", "commit", "diff", "tree", "blob",
- "snapshot", "tag", NULL};
+ static char *cmds[] = {"log", "commit", "diff", "tree", "view", "blob", "snapshot", NULL};
int i;
for(i = 0; cmds[i]; i++)
@@ -86,13 +82,6 @@ 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;
@@ -155,9 +144,7 @@ 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 = cgit_parse_snapshots_mask(value);
- else if (!strcmp(name, "enable-index-links"))
- cgit_enable_index_links = atoi(value);
+ cgit_snapshots = atoi(value);
else if (!strcmp(name, "enable-log-filecount"))
cgit_enable_log_filecount = atoi(value);
else if (!strcmp(name, "enable-log-linecount"))
@@ -197,7 +184,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 & cgit_parse_snapshots_mask(value); /* XXX: &? */
+ cgit_repo->snapshots = cgit_snapshots * atoi(value);
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"))
@@ -237,7 +224,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 = trim_end(value, '/');
+ cgit_query_path = xstrdup(value);
} else if (!strcmp(name, "name")) {
cgit_query_name = xstrdup(value);
}
@@ -266,28 +253,6 @@ 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)
{
@@ -394,7 +359,7 @@ void cgit_diff_tree(const unsigned char *old_sha1,
opt.format_callback_data = fn;
diff_setup_done(&opt);
- if (old_sha1 && !is_null_sha1(old_sha1))
+ if (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
new file mode 100755
index 0000000..1d7b13f
--- /dev/null
+++ b/submodules.sh
@@ -0,0 +1,181 @@
+#!/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
diff --git a/ui-commit.c b/ui-commit.c
index 90e09ed..1d12bbb 100644
--- a/ui-commit.c
+++ b/ui-commit.c
@@ -11,7 +11,6 @@
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;
@@ -28,6 +27,7 @@ static struct fileinfo {
void print_fileinfo(struct fileinfo *info)
{
+ char *query, *query2;
char *class;
switch (info->status) {
@@ -75,12 +75,24 @@ void print_fileinfo(struct fileinfo *info)
html("]</span>");
}
htmlf("</td><td class='%s'>", class);
- 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);
+ 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>");
+ }
html("</td><td class='right'>");
htmlf("%d", info->added + info->removed);
html("</td><td class='graph'>");
@@ -133,19 +145,16 @@ void inspect_filepair(struct diff_filepair *pair)
}
-void cgit_print_commit(char *hex)
+void cgit_print_commit(const char *hex)
{
struct commit *commit, *parent;
struct commitinfo *info;
struct commit_list *p;
unsigned char sha1[20];
- char *tmp;
+ char *query;
+ char *filename;
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;
@@ -172,11 +181,11 @@ void cgit_print_commit(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'>");
- tmp = xstrdup(hex);
- cgit_tree_link(sha1_to_hex(commit->tree->object.sha1), NULL, NULL,
- cgit_query_head, tmp, NULL);
- 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));
for (p = commit->parents; p ; p = p->next) {
parent = lookup_commit_reference(p->item->object.sha1);
if (!parent) {
@@ -186,19 +195,23 @@ void cgit_print_commit(char *hex)
continue;
}
html("<tr><th>parent</th>"
- "<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>");
+ "<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>");
}
if (cgit_repo->snapshots) {
- 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>");
+ 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("</table>\n");
html("<div class='commit-subject'>");
@@ -218,9 +231,10 @@ void cgit_print_commit(char *hex)
html("<div class='diffstat-summary'>");
htmlf("%d files changed, %d insertions, %d deletions (",
files, total_adds, total_rems);
- cgit_diff_link("show diff", NULL, NULL, cgit_query_head, hex,
- NULL, NULL);
- html(")</div>");
+ query = fmt("h=%s", hex);
+ html_link_open(cgit_pageurl(cgit_query_repo, "diff", query), NULL, NULL);
+ html("show diff</a>)");
+ html("</div>");
}
cgit_free_commitinfo(info);
}
diff --git a/ui-diff.c b/ui-diff.c
index 0be845f..4695e3a 100644
--- a/ui-diff.c
+++ b/ui-diff.c
@@ -89,52 +89,54 @@ static void filepair_cb(struct diff_filepair *pair)
cgit_print_error("Error running diff");
}
-void cgit_print_diff(const char *new_rev, const char *old_rev)
+void cgit_print_diff(const char *head, const char *old_hex, const char *new_hex, char *path)
{
unsigned char sha1[20], sha2[20];
enum object_type type;
unsigned long size;
- struct commit *commit, *commit2;
+ struct commit *commit;
- 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)));
+ 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>");
+ }
return;
}
- commit = lookup_commit_reference(sha1);
- if (!commit || parse_commit(commit))
- cgit_print_error(fmt("Bad commit: %s", sha1_to_hex(sha1)));
+ get_sha1(old_hex, sha1);
+ get_sha1(new_hex, sha2);
- 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(sha1, &size);
+ if (type == OBJ_BAD) {
type = sha1_object_info(sha2, &size);
if (type == OBJ_BAD) {
- cgit_print_error(fmt("Bad object name: %s", sha1_to_hex(sha2)));
+ cgit_print_error(fmt("Bad object names: %s, %s", old_hex, new_hex));
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'>");
- html("<tr><td>");
- cgit_diff_tree(sha2, sha1, filepair_cb);
- html("</td></tr>");
+ 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("</table>");
}
diff --git a/ui-log.c b/ui-log.c
index d38e40a..bb17e1d 100644
--- a/ui-log.c
+++ b/ui-log.c
@@ -31,8 +31,11 @@ void print_commit(struct commit *commit)
html("<tr><td>");
cgit_print_age(commit->date, TM_WEEK * 2, FMT_SHORTDATE);
html("</td><td>");
- cgit_commit_link(info->subject, NULL, NULL, cgit_query_head,
- sha1_to_hex(commit->object.sha1));
+ 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();
if (cgit_repo->enable_log_filecount) {
files = 0;
lines = 0;
@@ -59,9 +62,6 @@ 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,15 +113,17 @@ 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) {
- cgit_log_link("[prev]", NULL, NULL, cgit_query_head,
- cgit_query_sha1, cgit_query_path,
- ofs - cnt);
- html("&nbsp;");
+ 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;");
}
+
if ((commit = get_revision(&rev)) != NULL) {
- cgit_log_link("[next]", NULL, NULL, cgit_query_head,
- cgit_query_sha1, cgit_query_path,
- ofs + cnt);
+ html("&nbsp;<a href='");
+ html(cgit_pageurl(cgit_query_repo, "log",
+ fmt("h=%s&amp;ofs=%d", tip, ofs+cnt)));
+ html("'>[next]</a>&nbsp;");
}
html("</div>");
}
diff --git a/ui-repolist.c b/ui-repolist.c
index 4c86543..e5c6c20 100644
--- a/ui-repolist.c
+++ b/ui-repolist.c
@@ -44,19 +44,16 @@ static void print_modtime(struct repoinfo *repo)
void cgit_print_repolist(struct cacheitem *item)
{
- int i, columns = 4;
+ struct repoinfo *repo;
+ int i;
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) {
- htmlf("<tr class='nohover'><td colspan='%d' class='include-block'>",
- columns);
+ html("<tr class='nohover'><td colspan='5' class='include-block'>");
html_include(cgit_index_header);
html("</td></tr>");
}
@@ -64,45 +61,42 @@ 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>");
- if (cgit_enable_index_links)
- html("<th>Links</th>");
- html("</tr>\n");
+ "<th class='left'>Idle</th>"
+ "<th>Links</th></tr>\n");
for (i=0; i<cgit_repolist.count; i++) {
- 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);
+ 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);
html("</td></tr>");
- last_group = cgit_repo->group;
+ last_group = repo->group;
}
htmlf("<tr><td class='%s'>",
- cgit_repo->group ? "sublevel-repo" : "toplevel-repo");
- html_link_open(cgit_repourl(cgit_repo->url), NULL, NULL);
- html_txt(cgit_repo->name);
+ repo->group ? "sublevel-repo" : "toplevel-repo");
+ html_link_open(cgit_repourl(repo->url), repo->desc, NULL);
+ html_txt(repo->name);
html_link_close();
html("</td><td>");
- html_ntxt(cgit_max_repodesc_len, cgit_repo->desc);
+ html_ntxt(cgit_max_repodesc_len, repo->desc);
html("</td><td>");
- html_txt(cgit_repo->owner);
+ html_txt(repo->owner);
html("</td><td>");
- 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");
+ print_modtime(repo);
+ 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");
}
html("</table>");
cgit_print_docend();
diff --git a/ui-shared.c b/ui-shared.c
index 5c5bcf3..aba93e8 100644
--- a/ui-shared.c
+++ b/ui-shared.c
@@ -57,13 +57,13 @@ char *cgit_repourl(const char *reponame)
}
}
-char *cgit_fileurl(const char *reponame, const char *pagename,
- const char *filename, const char *query)
+char *cgit_pageurl(const char *reponame, const char *pagename,
+ const char *query)
{
if (cgit_virtual_root) {
if (query)
- return fmt("%s/%s/%s/%s?%s", cgit_virtual_root, reponame,
- pagename, filename?filename:"", query);
+ return fmt("%s/%s/%s/?%s", cgit_virtual_root, reponame,
+ pagename, query);
else
return fmt("%s/%s/%s/", cgit_virtual_root, reponame,
pagename);
@@ -75,37 +75,6 @@ char *cgit_fileurl(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)
@@ -118,166 +87,6 @@ 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)
{
@@ -343,7 +152,7 @@ void cgit_print_docstart(char *title, struct cacheitem *item)
html("<title>");
html_txt(title);
html("</title>\n");
- htmlf("<meta name='generator' content='cgit %s'/>\n", cgit_version);
+ htmlf("<meta name='generator' content='cgit v%s'/>\n", cgit_version);
html("<link rel='stylesheet' type='text/css' href='");
html_attr(cgit_css);
html("'/>\n");
@@ -360,38 +169,19 @@ void cgit_print_docend()
void cgit_print_pageheader(char *title, int show_search)
{
html("<table id='layout'>");
- html("<tr><td id='header'><a href='");
- html_attr(cgit_rooturl());
- html("'>");
- html_txt(cgit_root_title);
- html("</a></td><td id='logo'>");
+ html("<tr><td id='header'>");
+ html(cgit_root_title);
+ html("</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);
- 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");
+ htmlf("</a> : %s", title);
}
html("</td>");
html("<td id='search'>");
@@ -429,5 +219,3 @@ void cgit_print_snapshot_start(const char *mimetype, const char *filename,
ttl_seconds(item->ttl)));
html("\n");
}
-
-/* vim:set sw=8: */
diff --git a/ui-snapshot.c b/ui-snapshot.c
index bd34a28..2257d6b 100644
--- a/ui-snapshot.c
+++ b/ui-snapshot.c
@@ -8,148 +8,40 @@
#include "cgit.h"
-static int write_compressed_tar_archive(struct archiver_args *args,const char *filter)
+static void cgit_print_zip(struct cacheitem *item, const char *hex,
+ const char *prefix, const char *filename)
{
- 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);
- 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);
+ if (get_sha1(hex, sha1)) {
+ cgit_print_error(fmt("Bad object id: %s", hex));
return;
}
- cgit_print_error(fmt("Unsupported snapshot format: %s", filename));
-}
-
-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;
+ commit = lookup_commit_reference(sha1);
- 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/>");
+ 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("application/x-zip", filename, item);
+ write_zip_archive(&args);
}
-int cgit_parse_snapshots_mask(const char *str)
-{
- 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;
+void cgit_print_snapshot(struct cacheitem *item, const char *hex,
+ const char *format, const char *prefix,
+ const char *filename)
+{
+ if (!strcmp(format, "zip"))
+ cgit_print_zip(item, hex, prefix, filename);
+ else
+ cgit_print_error(fmt("Unsupported snapshot format: %s",
+ format));
}
-
-/* vim:set sw=8: */
diff --git a/ui-summary.c b/ui-summary.c
index de8a180..4bda4c2 100644
--- a/ui-summary.c
+++ b/ui-summary.c
@@ -15,10 +15,8 @@ static int cgit_print_branch_cb(const char *refname, const unsigned char *sha1,
{
struct commit *commit;
struct commitinfo *info;
- char buf[256];
- char *ref;
+ char buf[256], *url;
- ref = xstrdup(refname);
strncpy(buf, refname, sizeof(buf));
commit = lookup_commit(sha1);
// object is not really parsed at this point, because of some fallout
@@ -27,13 +25,21 @@ 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>");
- cgit_log_link(ref, NULL, NULL, ref, NULL, NULL, 0);
+ url = cgit_pageurl(cgit_query_repo, "log",
+ fmt("h=%s", refname));
+ html_link_open(url, NULL, NULL);
+ html_txt(buf);
+ html_link_close();
html("</td><td>");
cgit_print_age(commit->date, -1, NULL);
html("</td><td>");
html_txt(info->author);
html("</td><td>");
- cgit_commit_link(info->subject, NULL, NULL, ref, NULL);
+ 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();
html("</td></tr>\n");
cgit_free_commitinfo(info);
} else {
@@ -43,10 +49,33 @@ 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>"
@@ -75,8 +104,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, "tag",
- fmt("id=%s", refname));
+ url = cgit_pageurl(cgit_query_repo, "view",
+ fmt("id=%s", sha1_to_hex(sha1)));
html_link_open(url, NULL, NULL);
html_txt(buf);
html_link_close();
@@ -87,7 +116,7 @@ static int cgit_print_tag_cb(const char *refname, const unsigned char *sha1,
if (info->tagger)
html(info->tagger);
html("</td><td>");
- cgit_object_link(tag->tagged);
+ cgit_print_object_ref(tag->tagged);
html("</td></tr>\n");
} else {
if (!header)
@@ -95,7 +124,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_object_link(obj);
+ cgit_print_object_ref(obj);
html("</td></tr>\n");
}
return 0;
diff --git a/ui-tag.c b/ui-tag.c
deleted file mode 100644
index 6d761f3..0000000
--- a/ui-tag.c
+++ /dev/null
@@ -1,74 +0,0 @@
-/* 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;
-}
diff --git a/ui-tree.c b/ui-tree.c
index 1cb09f7..21dd533 100644
--- a/ui-tree.c
+++ b/ui-tree.c
@@ -9,64 +9,16 @@
#include "cgit.h"
char *curr_rev;
-char *match_path;
-int header = 0;
-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)
+static int print_entry(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>",
@@ -74,140 +26,79 @@ static int ls_item(const unsigned char *sha1, const char *base, int baselen,
sha1_to_hex(sha1));
return 0;
}
-
- html("<tr><td class='ls-mode'>");
+ html("<tr><td class='filemode'>");
html_filemode(mode);
- html("</td><td>");
+ html("</td><td ");
if (S_ISGITLINK(mode)) {
- htmlf("<a class='ls-mod' href='");
+ htmlf("class='ls-mod'><a href='");
html_attr(fmt(cgit_repo->module_link,
name,
sha1_to_hex(sha1)));
- html("'>");
- html_txt(name);
- html("</a>");
} else if (S_ISDIR(mode)) {
- cgit_tree_link(name, NULL, "ls-dir", cgit_query_head,
- curr_rev, fullpath);
+ 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)));
} else {
- cgit_tree_link(name, NULL, "ls-blob", cgit_query_head,
- curr_rev, fullpath);
+ 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)));
}
- htmlf("</td><td class='ls-size'>%li</td>", size);
+ htmlf("'>%s</a></td>", name);
+ htmlf("<td class='filesize'>%li</td>", size);
- html("<td>");
- cgit_log_link("log", NULL, "button", cgit_query_head, curr_rev,
- fullpath, 0);
- html("</td></tr>\n");
+ 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");
free(name);
return 0;
}
-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)
+void cgit_print_tree(const char *rev, const char *hex, 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);
- if (get_sha1(rev, sha1)) {
- cgit_print_error(fmt("Invalid revision name: %s", rev));
- return;
- }
+ get_sha1(rev, sha1);
commit = lookup_commit_reference(sha1);
if (!commit || parse_commit(commit)) {
- cgit_print_error(fmt("Invalid commit reference: %s", rev));
+ cgit_print_error(fmt("Invalid head: %s", rev));
return;
}
+ if (!hex)
+ hex = sha1_to_hex(commit->tree->object.sha1);
- 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);
+ 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));
return;
}
- match_path = path;
- read_tree_recursive(commit->tree, NULL, 0, 0, paths, walk_tree);
- ls_tail();
+ 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");
}
diff --git a/ui-view.c b/ui-view.c
new file mode 100644
index 0000000..8873415
--- /dev/null
+++ b/ui-view.c
@@ -0,0 +1,55 @@
+/* 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");
+}