summaryrefslogtreecommitdiffabout
authorLars Hjemli <hjemli@gmail.com>2008-08-06 07:50:10 (UTC)
committer Lars Hjemli <hjemli@gmail.com>2008-08-06 07:50:10 (UTC)
commite352a013aed6e925a10a92916500c7deccf1410a (patch) (side-by-side diff)
tree7c49bf453bee4f624025d62bae92b4926bf83bfe
parent3c71f597cc932992d5c44196e90f4675a1d54e77 (diff)
parentb2a3d31e8839b53a623b4c99124c2c637d0e3cbb (diff)
downloadcgit-e352a013aed6e925a10a92916500c7deccf1410a.zip
cgit-e352a013aed6e925a10a92916500c7deccf1410a.tar.gz
cgit-e352a013aed6e925a10a92916500c7deccf1410a.tar.bz2
Merge branch 'lh/atom'
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--Makefile1
-rw-r--r--cgit.h1
-rw-r--r--cmd.c7
-rw-r--r--ui-atom.c129
-rw-r--r--ui-atom.h6
-rw-r--r--ui-shared.c23
-rw-r--r--ui-shared.h1
7 files changed, 168 insertions, 0 deletions
diff --git a/Makefile b/Makefile
index e1436a3..6458431 100644
--- a/Makefile
+++ b/Makefile
@@ -1,124 +1,125 @@
CGIT_VERSION = v0.7.2
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.6
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
#
# Define a way to invoke make in subdirs quietly, shamelessly ripped
# from git.git
#
QUIET_SUBDIR0 = +$(MAKE) -C # space to separate -C and subdir
QUIET_SUBDIR1 =
ifneq ($(findstring $(MAKEFLAGS),w),w)
PRINT_DIR = --no-print-directory
else # "make -w"
NO_SUBDIR = :
endif
ifndef V
QUIET_CC = @echo ' ' CC $@;
QUIET_MM = @echo ' ' MM $@;
QUIET_SUBDIR0 = +@subdir=
QUIET_SUBDIR1 = ;$(NO_SUBDIR) echo ' ' SUBDIR $$subdir; \
$(MAKE) $(PRINT_DIR) -C $$subdir
endif
#
# Define a pattern rule for automatic dependency building
#
%.d: %.c
$(QUIET_MM)$(CC) $(CFLAGS) -MM $< | sed -e 's/\($*\)\.o:/\1.o $@:/g' >$@
#
# Define a pattern rule for silent object building
#
%.o: %.c
$(QUIET_CC)$(CC) -o $*.o -c $(CFLAGS) $<
EXTLIBS = git/libgit.a git/xdiff/lib.a -lz -lcrypto
OBJECTS =
OBJECTS += cache.o
OBJECTS += cgit.o
OBJECTS += cmd.o
OBJECTS += configfile.o
OBJECTS += html.o
OBJECTS += parsing.o
OBJECTS += shared.o
+OBJECTS += ui-atom.o
OBJECTS += ui-blob.o
OBJECTS += ui-commit.o
OBJECTS += ui-diff.o
OBJECTS += ui-log.o
OBJECTS += ui-patch.o
OBJECTS += ui-refs.o
OBJECTS += ui-repolist.o
OBJECTS += ui-shared.o
OBJECTS += ui-snapshot.o
OBJECTS += ui-summary.o
OBJECTS += ui-tag.o
OBJECTS += ui-tree.o
ifdef NEEDS_LIBICONV
EXTLIBS += -liconv
endif
.PHONY: all git test install uninstall clean force-version get-git
all: cgit
VERSION: force-version
@./gen-version.sh "$(CGIT_VERSION)"
-include VERSION
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)"'
CFLAGS += -DCGIT_CACHE_ROOT='"$(CACHE_ROOT)"'
cgit: $(OBJECTS) git/libgit.a git/xdiff/lib.a
$(QUIET_CC)$(CC) $(CFLAGS) -o cgit $(OBJECTS) $(EXTLIBS)
cgit.o: VERSION
-include $(OBJECTS:.o=.d)
git/libgit.a: git
$(QUIET_SUBDIR0)git $(QUIET_SUBDIR1) libgit.a
git/xdiff/lib.a: git
$(QUIET_SUBDIR0)git $(QUIET_SUBDIR1) xdiff/lib.a
test: all
$(QUIET_SUBDIR0)tests $(QUIET_SUBDIR1) all
install: all
mkdir -p $(DESTDIR)$(CGIT_SCRIPT_PATH)
install cgit $(DESTDIR)$(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME)
install cgit.css $(DESTDIR)$(CGIT_SCRIPT_PATH)/cgit.css
install cgit.png $(DESTDIR)$(CGIT_SCRIPT_PATH)/cgit.png
uninstall:
rm -f $(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME)
rm -f $(CGIT_SCRIPT_PATH)/cgit.css
rm -f $(CGIT_SCRIPT_PATH)/cgit.png
clean:
rm -f cgit VERSION *.o *.d
get-git:
curl $(GIT_URL) | tar -xj && rm -rf git && mv git-$(GIT_VER) git
diff --git a/cgit.h b/cgit.h
index b01fa31..a1fa841 100644
--- a/cgit.h
+++ b/cgit.h
@@ -1,239 +1,240 @@
#ifndef CGIT_H
#define CGIT_H
#include <git-compat-util.h>
#include <cache.h>
#include <grep.h>
#include <object.h>
#include <tree.h>
#include <commit.h>
#include <tag.h>
#include <diff.h>
#include <diffcore.h>
#include <refs.h>
#include <revision.h>
#include <log-tree.h>
#include <archive.h>
#include <xdiff/xdiff.h>
#include <utf8.h>
/*
* Dateformats used on misc. pages
*/
#define FMT_LONGDATE "%Y-%m-%d %H:%M:%S (%Z)"
#define FMT_SHORTDATE "%Y-%m-%d"
+#define FMT_ATOMDATE "%Y-%m-%dT%H:%M:%SZ"
/*
* Limits used for relative dates
*/
#define TM_MIN 60
#define TM_HOUR (TM_MIN * 60)
#define TM_DAY (TM_HOUR * 24)
#define TM_WEEK (TM_DAY * 7)
#define TM_YEAR (TM_DAY * 365)
#define TM_MONTH (TM_YEAR / 12.0)
/*
* Default encoding
*/
#define PAGE_ENCODING "UTF-8"
typedef void (*configfn)(const char *name, const char *value);
typedef void (*filepair_fn)(struct diff_filepair *pair);
typedef void (*linediff_fn)(char *line, int len);
struct cgit_repo {
char *url;
char *name;
char *path;
char *desc;
char *owner;
char *defbranch;
char *group;
char *module_link;
char *readme;
char *clone_url;
int snapshots;
int enable_log_filecount;
int enable_log_linecount;
};
struct cgit_repolist {
int length;
int count;
struct cgit_repo *repos;
};
struct commitinfo {
struct commit *commit;
char *author;
char *author_email;
unsigned long author_date;
char *committer;
char *committer_email;
unsigned long committer_date;
char *subject;
char *msg;
char *msg_encoding;
};
struct taginfo {
char *tagger;
char *tagger_email;
int tagger_date;
char *msg;
};
struct refinfo {
const char *refname;
struct object *object;
union {
struct taginfo *tag;
struct commitinfo *commit;
};
};
struct reflist {
struct refinfo **refs;
int alloc;
int count;
};
struct cgit_query {
int has_symref;
int has_sha1;
char *raw;
char *repo;
char *page;
char *search;
char *grep;
char *head;
char *sha1;
char *sha2;
char *path;
char *name;
char *mimetype;
int ofs;
};
struct cgit_config {
char *agefile;
char *cache_root;
char *clone_prefix;
char *css;
char *favicon;
char *footer;
char *index_header;
char *index_info;
char *logo;
char *logo_link;
char *module_link;
char *repo_group;
char *robots;
char *root_title;
char *root_desc;
char *root_readme;
char *script_name;
char *virtual_root;
int cache_size;
int cache_dynamic_ttl;
int cache_max_create_time;
int cache_repo_ttl;
int cache_root_ttl;
int cache_static_ttl;
int enable_index_links;
int enable_log_filecount;
int enable_log_linecount;
int local_time;
int max_repo_count;
int max_commit_count;
int max_lock_attempts;
int max_msg_len;
int max_repodesc_len;
int nocache;
int renamelimit;
int snapshots;
int summary_branches;
int summary_log;
int summary_tags;
};
struct cgit_page {
time_t modified;
time_t expires;
char *mimetype;
char *charset;
char *filename;
char *title;
};
struct cgit_context {
struct cgit_query qry;
struct cgit_config cfg;
struct cgit_repo *repo;
struct cgit_page page;
};
struct cgit_snapshot_format {
const char *suffix;
const char *mimetype;
write_archive_fn_t write_func;
int bit;
};
extern const char *cgit_version;
extern struct cgit_repolist cgit_repolist;
extern struct cgit_context ctx;
extern const struct cgit_snapshot_format cgit_snapshot_formats[];
extern struct cgit_repo *cgit_add_repo(const char *url);
extern struct cgit_repo *cgit_get_repoinfo(const char *url);
extern void cgit_repo_config_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 char *trim_end(const char *str, char c);
extern char *strlpart(char *txt, int maxlen);
extern char *strrpart(char *txt, int maxlen);
extern void cgit_add_ref(struct reflist *list, struct refinfo *ref);
extern int cgit_refs_cb(const char *refname, const unsigned char *sha1,
int flags, void *cb_data);
extern void *cgit_free_commitinfo(struct commitinfo *info);
extern int cgit_diff_files(const unsigned char *old_sha1,
const unsigned char *new_sha1,
linediff_fn fn);
extern void cgit_diff_tree(const unsigned char *old_sha1,
const unsigned char *new_sha1,
filepair_fn fn, const char *prefix);
extern void cgit_diff_commit(struct commit *commit, filepair_fn fn);
extern char *fmt(const char *format,...);
extern struct commitinfo *cgit_parse_commit(struct commit *commit);
extern struct taginfo *cgit_parse_tag(struct tag *tag);
extern void cgit_parse_url(const char *url);
extern const char *cgit_repobasename(const char *reponame);
extern int cgit_parse_snapshots_mask(const char *str);
/* libgit.a either links against or compiles its own implementation of
* strcasestr(), and we'd like to reuse it. Simply re-declaring it
* seems to do the trick.
*/
extern char *strcasestr(const char *haystack, const char *needle);
#endif /* CGIT_H */
diff --git a/cmd.c b/cmd.c
index fe0ea8f..c0e4db3 100644
--- a/cmd.c
+++ b/cmd.c
@@ -1,132 +1,139 @@
/* cmd.c: the cgit command dispatcher
*
* Copyright (C) 2008 Lars Hjemli
*
* Licensed under GNU General Public License v2
* (see COPYING for full license text)
*/
#include "cgit.h"
#include "cmd.h"
#include "cache.h"
#include "ui-shared.h"
+#include "ui-atom.h"
#include "ui-blob.h"
#include "ui-commit.h"
#include "ui-diff.h"
#include "ui-log.h"
#include "ui-patch.h"
#include "ui-refs.h"
#include "ui-repolist.h"
#include "ui-snapshot.h"
#include "ui-summary.h"
#include "ui-tag.h"
#include "ui-tree.h"
+static void atom_fn(struct cgit_context *ctx)
+{
+ cgit_print_atom(ctx->qry.head, ctx->qry.path, 10);
+}
+
static void about_fn(struct cgit_context *ctx)
{
if (ctx->repo)
cgit_print_repo_readme();
else
cgit_print_site_readme();
}
static void blob_fn(struct cgit_context *ctx)
{
cgit_print_blob(ctx->qry.sha1, ctx->qry.path, ctx->qry.head);
}
static void commit_fn(struct cgit_context *ctx)
{
cgit_print_commit(ctx->qry.sha1);
}
static void diff_fn(struct cgit_context *ctx)
{
cgit_print_diff(ctx->qry.sha1, ctx->qry.sha2, ctx->qry.path);
}
static void log_fn(struct cgit_context *ctx)
{
cgit_print_log(ctx->qry.sha1, ctx->qry.ofs, ctx->cfg.max_commit_count,
ctx->qry.grep, ctx->qry.search, ctx->qry.path, 1);
}
static void ls_cache_fn(struct cgit_context *ctx)
{
ctx->page.mimetype = "text/plain";
ctx->page.filename = "ls-cache.txt";
cgit_print_http_headers(ctx);
cache_ls(ctx->cfg.cache_root);
}
static void repolist_fn(struct cgit_context *ctx)
{
cgit_print_repolist();
}
static void patch_fn(struct cgit_context *ctx)
{
cgit_print_patch(ctx->qry.sha1);
}
static void refs_fn(struct cgit_context *ctx)
{
cgit_print_refs();
}
static void snapshot_fn(struct cgit_context *ctx)
{
cgit_print_snapshot(ctx->qry.head, ctx->qry.sha1,
cgit_repobasename(ctx->repo->url), ctx->qry.path,
ctx->repo->snapshots);
}
static void summary_fn(struct cgit_context *ctx)
{
cgit_print_summary();
}
static void tag_fn(struct cgit_context *ctx)
{
cgit_print_tag(ctx->qry.sha1);
}
static void tree_fn(struct cgit_context *ctx)
{
cgit_print_tree(ctx->qry.sha1, ctx->qry.path);
}
#define def_cmd(name, want_repo, want_layout) \
{#name, name##_fn, want_repo, want_layout}
struct cgit_cmd *cgit_get_cmd(struct cgit_context *ctx)
{
static struct cgit_cmd cmds[] = {
+ def_cmd(atom, 1, 0),
def_cmd(about, 0, 1),
def_cmd(blob, 1, 0),
def_cmd(commit, 1, 1),
def_cmd(diff, 1, 1),
def_cmd(log, 1, 1),
def_cmd(ls_cache, 0, 0),
def_cmd(patch, 1, 0),
def_cmd(refs, 1, 1),
def_cmd(repolist, 0, 0),
def_cmd(snapshot, 1, 0),
def_cmd(summary, 1, 1),
def_cmd(tag, 1, 1),
def_cmd(tree, 1, 1),
};
int i;
if (ctx->qry.page == NULL) {
if (ctx->repo)
ctx->qry.page = "summary";
else
ctx->qry.page = "repolist";
}
for(i = 0; i < sizeof(cmds)/sizeof(*cmds); i++)
if (!strcmp(ctx->qry.page, cmds[i].name))
return &cmds[i];
return NULL;
}
diff --git a/ui-atom.c b/ui-atom.c
new file mode 100644
index 0000000..a6ea3ee
--- a/dev/null
+++ b/ui-atom.c
@@ -0,0 +1,129 @@
+/* ui-atom.c: functions for atom feeds
+ *
+ * Copyright (C) 2008 Lars Hjemli
+ *
+ * Licensed under GNU General Public License v2
+ * (see COPYING for full license text)
+ */
+
+#include "cgit.h"
+#include "html.h"
+#include "ui-shared.h"
+
+void add_entry(struct commit *commit, char *host)
+{
+ char delim = '&';
+ char *hex;
+ char *mail, *t, *t2;
+ struct commitinfo *info;
+
+ info = cgit_parse_commit(commit);
+ hex = sha1_to_hex(commit->object.sha1);
+ html("<entry>\n");
+ html("<title>");
+ html_txt(info->subject);
+ html("</title>\n");
+ html("<updated>");
+ cgit_print_date(info->author_date, FMT_ATOMDATE, ctx.cfg.local_time);
+ html("</updated>\n");
+ html("<author>\n");
+ if (info->author) {
+ html("<name>");
+ html_txt(info->author);
+ html("</name>\n");
+ }
+ if (info->author_email) {
+ mail = xstrdup(info->author_email);
+ t = strchr(mail, '<');
+ if (t)
+ t++;
+ else
+ t = mail;
+ t2 = strchr(t, '>');
+ if (t2)
+ *t2 = '\0';
+ html("<email>");
+ html_txt(t);
+ html("</email>\n");
+ free(mail);
+ }
+ html("</author>\n");
+ html("<published>");
+ cgit_print_date(info->author_date, FMT_ATOMDATE, ctx.cfg.local_time);
+ html("</published>\n");
+ if (host) {
+ html("<link rel='alternate' type='text/html' href='http://");
+ html_attr(host);
+ html_attr(cgit_pageurl(ctx.repo->url, "commit", NULL));
+ if (ctx.cfg.virtual_root)
+ delim = '?';
+ htmlf("%cid=%s", delim, hex);
+ html("'/>\n");
+ }
+ htmlf("<id>%s</id>\n", hex);
+ html("<content type='text'>\n");
+ html_txt(info->msg);
+ html("</content>\n");
+ html("<content type='xhtml'>\n");
+ html("<div xmlns='http://www.w3.org/1999/xhtml'>\n");
+ html("<pre>\n");
+ html_txt(info->msg);
+ html("</pre>\n");
+ html("</div>\n");
+ html("</content>\n");
+ html("</entry>\n");
+ cgit_free_commitinfo(info);
+}
+
+
+void cgit_print_atom(char *tip, char *path, int max_count)
+{
+ char *host;
+ const char *argv[] = {NULL, tip, NULL, NULL, NULL};
+ struct commit *commit;
+ struct rev_info rev;
+ int argc = 2;
+
+ if (!tip)
+ argv[1] = ctx.qry.head;
+
+ if (path) {
+ argv[argc++] = "--";
+ argv[argc++] = path;
+ }
+
+ init_revisions(&rev, NULL);
+ rev.abbrev = DEFAULT_ABBREV;
+ rev.commit_format = CMIT_FMT_DEFAULT;
+ rev.verbose_header = 1;
+ rev.show_root_diff = 0;
+ rev.max_count = max_count;
+ setup_revisions(argc, argv, &rev, NULL);
+ prepare_revision_walk(&rev);
+
+ host = cgit_hosturl();
+ ctx.page.mimetype = "text/xml";
+ ctx.page.charset = "utf-8";
+ cgit_print_http_headers(&ctx);
+ html("<feed xmlns='http://www.w3.org/2005/Atom'>\n");
+ html("<title>");
+ html_txt(ctx.repo->name);
+ html("</title>\n");
+ html("<subtitle>");
+ html_txt(ctx.repo->desc);
+ html("</subtitle>\n");
+ if (host) {
+ html("<link rel='alternate' type='text/html' href='http://");
+ html_attr(host);
+ html_attr(cgit_repourl(ctx.repo->url));
+ html("'/>\n");
+ }
+ while ((commit = get_revision(&rev)) != NULL) {
+ add_entry(commit, host);
+ free(commit->buffer);
+ commit->buffer = NULL;
+ free_commit_list(commit->parents);
+ commit->parents = NULL;
+ }
+ html("</feed>\n");
+}
diff --git a/ui-atom.h b/ui-atom.h
new file mode 100644
index 0000000..749ffd3
--- a/dev/null
+++ b/ui-atom.h
@@ -0,0 +1,6 @@
+#ifndef UI_ATOM_H
+#define UI_ATOM_H
+
+extern void cgit_print_atom(char *tip, char *path, int max_count);
+
+#endif
diff --git a/ui-shared.c b/ui-shared.c
index 197ee37..37c60b2 100644
--- a/ui-shared.c
+++ b/ui-shared.c
@@ -1,676 +1,699 @@
/* ui-shared.c: common web output functions
*
* Copyright (C) 2006 Lars Hjemli
*
* Licensed under GNU General Public License v2
* (see COPYING for full license text)
*/
#include "cgit.h"
#include "cmd.h"
#include "html.h"
const char cgit_doctype[] =
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n"
" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n";
static char *http_date(time_t t)
{
static char day[][4] =
{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
static char month[][4] =
{"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Now", "Dec"};
struct tm *tm = gmtime(&t);
return fmt("%s, %02d %s %04d %02d:%02d:%02d GMT", day[tm->tm_wday],
tm->tm_mday, month[tm->tm_mon], 1900+tm->tm_year,
tm->tm_hour, tm->tm_min, tm->tm_sec);
}
void cgit_print_error(char *msg)
{
html("<div class='error'>");
html_txt(msg);
html("</div>\n");
}
+char *cgit_hosturl()
+{
+ char *host, *port;
+
+ host = getenv("SERVER_NAME");
+ if (!host)
+ return NULL;
+ port = getenv("SERVER_PORT");
+ if (port && atoi(port) != 80)
+ host = xstrdup(fmt("%s:%d", host, atoi(port)));
+ else
+ host = xstrdup(host);
+ return host;
+}
+
char *cgit_rooturl()
{
if (ctx.cfg.virtual_root)
return fmt("%s/", ctx.cfg.virtual_root);
else
return ctx.cfg.script_name;
}
char *cgit_repourl(const char *reponame)
{
if (ctx.cfg.virtual_root) {
return fmt("%s/%s/", ctx.cfg.virtual_root, reponame);
} else {
return fmt("?r=%s", reponame);
}
}
char *cgit_fileurl(const char *reponame, const char *pagename,
const char *filename, const char *query)
{
char *tmp;
char *delim;
if (ctx.cfg.virtual_root) {
tmp = fmt("%s/%s/%s/%s", ctx.cfg.virtual_root, reponame,
pagename, (filename ? filename:""));
delim = "?";
} else {
tmp = fmt("?url=%s/%s/%s", reponame, pagename,
(filename ? filename : ""));
delim = "&";
}
if (query)
tmp = fmt("%s%s%s", tmp, delim, query);
return tmp;
}
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 (!ctx.cfg.virtual_root)
return ctx.cfg.script_name;
else if (ctx.qry.page)
return fmt("%s/%s/%s/", ctx.cfg.virtual_root, ctx.qry.repo, ctx.qry.page);
else if (ctx.qry.repo)
return fmt("%s/%s/", ctx.cfg.virtual_root, ctx.qry.repo);
else
return fmt("%s/", ctx.cfg.virtual_root);
}
static void site_url(char *page, char *search, int ofs)
{
char *delim = "?";
if (ctx.cfg.virtual_root) {
html_attr(ctx.cfg.virtual_root);
if (ctx.cfg.virtual_root[strlen(ctx.cfg.virtual_root) - 1] != '/')
html("/");
} else
html(ctx.cfg.script_name);
if (page) {
htmlf("?p=%s", page);
delim = "&";
}
if (search) {
html(delim);
html("q=");
html_attr(search);
delim = "&";
}
if (ofs) {
html(delim);
htmlf("ofs=%d", ofs);
}
}
static void site_link(char *page, char *name, char *title, char *class,
char *search, int ofs)
{
html("<a");
if (title) {
html(" title='");
html_attr(title);
html("'");
}
if (class) {
html(" class='");
html_attr(class);
html("'");
}
html(" href='");
site_url(page, search, ofs);
html("'>");
html_txt(name);
html("</a>");
}
void cgit_index_link(char *name, char *title, char *class, char *pattern,
int ofs)
{
site_link(NULL, name, title, class, pattern, ofs);
}
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 (ctx.cfg.virtual_root) {
html_attr(ctx.cfg.virtual_root);
if (ctx.cfg.virtual_root[strlen(ctx.cfg.virtual_root) - 1] != '/')
html("/");
html_attr(ctx.repo->url);
if (ctx.repo->url[strlen(ctx.repo->url) - 1] != '/')
html("/");
if (page) {
html(page);
html("/");
if (path)
html_attr(path);
}
} else {
html(ctx.cfg.script_name);
html("?url=");
html_attr(ctx.repo->url);
if (ctx.repo->url[strlen(ctx.repo->url) - 1] != '/')
html("/");
if (page) {
html(page);
html("/");
if (path)
html_attr(path);
}
delim = "&amp;";
}
if (head && strcmp(head, ctx.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, ctx.qry.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 *grep, char *pattern)
{
char *delim;
delim = repolink(title, class, "log", head, path);
if (rev && strcmp(rev, ctx.qry.head)) {
html(delim);
html("id=");
html_attr(rev);
delim = "&";
}
if (grep && pattern) {
html(delim);
html("qt=");
html_attr(grep);
delim = "&";
html(delim);
html("q=");
html_attr(pattern);
}
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) > ctx.cfg.max_msg_len && ctx.cfg.max_msg_len >= 15) {
name[ctx.cfg.max_msg_len] = '\0';
name[ctx.cfg.max_msg_len - 1] = '.';
name[ctx.cfg.max_msg_len - 2] = '.';
name[ctx.cfg.max_msg_len - 3] = '.';
}
reporevlink("commit", name, title, class, head, rev, NULL);
}
void cgit_refs_link(char *name, char *title, char *class, char *head,
char *rev, char *path)
{
reporevlink("refs", name, title, class, head, rev, path);
}
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, ctx.qry.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_patch_link(char *name, char *title, char *class, char *head,
char *rev)
{
reporevlink("patch", name, title, class, head, rev, NULL);
}
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,
ctx.qry.head, sha1_to_hex(obj->sha1));
return;
} else if (obj->type == OBJ_TREE) {
page = "tree";
arg = "id";
} else if (obj->type == OBJ_TAG) {
page = "tag";
arg = "id";
} else {
page = "blob";
arg = "id";
}
url = cgit_pageurl(ctx.qry.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, int local_time)
{
char buf[64];
struct tm *time;
if (!secs)
return;
if(local_time)
time = localtime(&secs);
else
time = gmtime(&secs);
strftime(buf, sizeof(buf)-1, format, time);
html_txt(buf);
}
void cgit_print_age(time_t t, time_t max_relative, char *format)
{
time_t now, secs;
if (!t)
return;
time(&now);
secs = now - t;
if (secs > max_relative && max_relative >= 0) {
cgit_print_date(t, format, ctx.cfg.local_time);
return;
}
if (secs < TM_HOUR * 2) {
htmlf("<span class='age-mins'>%.0f min.</span>",
secs * 1.0 / TM_MIN);
return;
}
if (secs < TM_DAY * 2) {
htmlf("<span class='age-hours'>%.0f hours</span>",
secs * 1.0 / TM_HOUR);
return;
}
if (secs < TM_WEEK * 2) {
htmlf("<span class='age-days'>%.0f days</span>",
secs * 1.0 / TM_DAY);
return;
}
if (secs < TM_MONTH * 2) {
htmlf("<span class='age-weeks'>%.0f weeks</span>",
secs * 1.0 / TM_WEEK);
return;
}
if (secs < TM_YEAR * 2) {
htmlf("<span class='age-months'>%.0f months</span>",
secs * 1.0 / TM_MONTH);
return;
}
htmlf("<span class='age-years'>%.0f years</span>",
secs * 1.0 / TM_YEAR);
}
void cgit_print_http_headers(struct cgit_context *ctx)
{
if (ctx->page.mimetype && ctx->page.charset)
htmlf("Content-Type: %s; charset=%s\n", ctx->page.mimetype,
ctx->page.charset);
else if (ctx->page.mimetype)
htmlf("Content-Type: %s\n", ctx->page.mimetype);
if (ctx->page.filename)
htmlf("Content-Disposition: inline; filename=\"%s\"\n",
ctx->page.filename);
htmlf("Last-Modified: %s\n", http_date(ctx->page.modified));
htmlf("Expires: %s\n", http_date(ctx->page.expires));
html("\n");
}
void cgit_print_docstart(struct cgit_context *ctx)
{
+ char *host = cgit_hosturl();
html(cgit_doctype);
html("<html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en' lang='en'>\n");
html("<head>\n");
html("<title>");
html_txt(ctx->page.title);
html("</title>\n");
htmlf("<meta name='generator' content='cgit %s'/>\n", cgit_version);
if (ctx->cfg.robots && *ctx->cfg.robots)
htmlf("<meta name='robots' content='%s'/>\n", ctx->cfg.robots);
html("<link rel='stylesheet' type='text/css' href='");
html_attr(ctx->cfg.css);
html("'/>\n");
if (ctx->cfg.favicon) {
html("<link rel='shortcut icon' href='");
html_attr(ctx->cfg.favicon);
html("'/>\n");
}
+ if (host && ctx->repo) {
+ html("<link rel='alternate' title='Atom feed' href='http://");
+ html_attr(cgit_hosturl());
+ html_attr(cgit_fileurl(ctx->repo->url, "atom", ctx->qry.path,
+ fmt("h=%s", ctx->qry.head)));
+ html("' type='application/atom+xml'/>");
+ }
html("</head>\n");
html("<body>\n");
}
void cgit_print_docend()
{
html("</div>");
if (ctx.cfg.footer)
html_include(ctx.cfg.footer);
else {
html("<div class='footer'>generated ");
cgit_print_date(time(NULL), FMT_LONGDATE, ctx.cfg.local_time);
htmlf(" by cgit %s", cgit_version);
html("</div>\n");
}
html("</body>\n</html>\n");
}
int print_branch_option(const char *refname, const unsigned char *sha1,
int flags, void *cb_data)
{
char *name = (char *)refname;
html_option(name, name, ctx.qry.head);
return 0;
}
int print_archive_ref(const char *refname, const unsigned char *sha1,
int flags, void *cb_data)
{
struct tag *tag;
struct taginfo *info;
struct object *obj;
char buf[256], *url;
unsigned char fileid[20];
int *header = (int *)cb_data;
if (prefixcmp(refname, "refs/archives"))
return 0;
strncpy(buf, refname+14, sizeof(buf));
obj = parse_object(sha1);
if (!obj)
return 1;
if (obj->type == OBJ_TAG) {
tag = lookup_tag(sha1);
if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag)))
return 0;
hashcpy(fileid, tag->tagged->sha1);
} else if (obj->type != OBJ_BLOB) {
return 0;
} else {
hashcpy(fileid, sha1);
}
if (!*header) {
html("<h1>download</h1>\n");
*header = 1;
}
url = cgit_pageurl(ctx.qry.repo, "blob",
fmt("id=%s&amp;path=%s", sha1_to_hex(fileid),
buf));
html_link_open(url, NULL, "menu");
html_txt(strlpart(buf, 20));
html_link_close();
return 0;
}
void add_hidden_formfields(int incl_head, int incl_search, char *page)
{
char *url;
if (!ctx.cfg.virtual_root) {
url = fmt("%s/%s", ctx.qry.repo, page);
if (ctx.qry.path)
url = fmt("%s/%s", url, ctx.qry.path);
html_hidden("url", url);
}
if (incl_head && ctx.qry.head && ctx.repo->defbranch &&
strcmp(ctx.qry.head, ctx.repo->defbranch))
html_hidden("h", ctx.qry.head);
if (ctx.qry.sha1)
html_hidden("id", ctx.qry.sha1);
if (ctx.qry.sha2)
html_hidden("id2", ctx.qry.sha2);
if (incl_search) {
if (ctx.qry.grep)
html_hidden("qt", ctx.qry.grep);
if (ctx.qry.search)
html_hidden("q", ctx.qry.search);
}
}
char *hc(struct cgit_cmd *cmd, const char *page)
{
return (strcmp(cmd->name, page) ? NULL : "active");
}
void cgit_print_pageheader(struct cgit_context *ctx)
{
struct cgit_cmd *cmd = cgit_get_cmd(ctx);
html("<table id='header'>\n");
html("<tr>\n");
html("<td class='logo' rowspan='2'><a href='");
if (ctx->cfg.logo_link)
html_attr(ctx->cfg.logo_link);
else
html_attr(cgit_rooturl());
html("'><img src='");
html_attr(ctx->cfg.logo);
html("' alt='cgit logo'/></a></td>\n");
html("<td class='main'>");
if (ctx->repo) {
cgit_index_link("index", NULL, NULL, NULL, 0);
html(" : ");
reporevlink(NULL, ctx->repo->name, NULL, hc(cmd, "summary"),
ctx->qry.head, NULL, NULL);
html("</td><td class='form'>");
html("<form method='get' action=''>\n");
add_hidden_formfields(0, 1, ctx->qry.page);
html("<select name='h' onchange='this.form.submit();'>\n");
for_each_branch_ref(print_branch_option, ctx->qry.head);
html("</select> ");
html("<input type='submit' name='' value='switch'/>");
html("</form>");
} else
html_txt(ctx->cfg.root_title);
html("</td></tr>\n");
html("<tr><td class='sub'>");
if (ctx->repo) {
html_txt(ctx->repo->desc);
html("</td><td class='sub right'>");
html_txt(ctx->repo->owner);
} else {
if (ctx->cfg.root_desc)
html_txt(ctx->cfg.root_desc);
else if (ctx->cfg.index_info)
html_include(ctx->cfg.index_info);
}
html("</td></tr></table>\n");
html("<table class='tabs'><tr><td>\n");
if (ctx->repo) {
reporevlink(NULL, "summary", NULL, hc(cmd, "summary"),
ctx->qry.head, NULL, NULL);
cgit_refs_link("refs", NULL, hc(cmd, "refs"), ctx->qry.head,
ctx->qry.sha1, NULL);
cgit_log_link("log", NULL, hc(cmd, "log"), ctx->qry.head,
NULL, NULL, 0, NULL, NULL);
cgit_tree_link("tree", NULL, hc(cmd, "tree"), ctx->qry.head,
ctx->qry.sha1, NULL);
cgit_commit_link("commit", NULL, hc(cmd, "commit"),
ctx->qry.head, ctx->qry.sha1);
cgit_diff_link("diff", NULL, hc(cmd, "diff"), ctx->qry.head,
ctx->qry.sha1, ctx->qry.sha2, NULL);
if (ctx->repo->readme)
reporevlink("about", "about", NULL,
hc(cmd, "about"), ctx->qry.head, NULL,
NULL);
html("</td><td class='form'>");
html("<form class='right' method='get' action='");
if (ctx->cfg.virtual_root)
html_attr(cgit_fileurl(ctx->qry.repo, "log",
ctx->qry.path, NULL));
html("'>\n");
add_hidden_formfields(1, 0, "log");
html("<select name='qt'>\n");
html_option("grep", "log msg", ctx->qry.grep);
html_option("author", "author", ctx->qry.grep);
html_option("committer", "committer", ctx->qry.grep);
html("</select>\n");
html("<input class='txt' type='text' size='10' name='q' value='");
html_attr(ctx->qry.search);
html("'/>\n");
html("<input type='submit' value='search'/>\n");
html("</form>\n");
} else {
site_link(NULL, "index", NULL, hc(cmd, "repolist"), NULL, 0);
if (ctx->cfg.root_readme)
site_link("about", "about", NULL, hc(cmd, "about"),
NULL, 0);
html("</td><td class='form'>");
html("<form method='get' action='");
html_attr(cgit_rooturl());
html("'>\n");
html("<input type='text' name='q' size='10' value='");
html_attr(ctx->qry.search);
html("'/>\n");
html("<input type='submit' value='search'/>\n");
html("</form>");
}
html("</td></tr></table>\n");
html("<div class='content'>");
}
void cgit_print_filemode(unsigned short mode)
{
if (S_ISDIR(mode))
html("d");
else if (S_ISLNK(mode))
html("l");
else if (S_ISGITLINK(mode))
html("m");
else
html("-");
html_fileperm(mode >> 6);
html_fileperm(mode >> 3);
html_fileperm(mode);
}
void cgit_print_snapshot_links(const char *repo, const char *head,
const char *hex, int snapshots)
{
const struct cgit_snapshot_format* f;
char *filename;
for (f = cgit_snapshot_formats; f->suffix; f++) {
if (!(snapshots & f->bit))
continue;
filename = fmt("%s-%s%s", cgit_repobasename(repo), hex,
f->suffix);
cgit_snapshot_link(filename, NULL, NULL, (char *)head,
(char *)hex, filename);
html("<br/>");
}
}
diff --git a/ui-shared.h b/ui-shared.h
index 07da4b4..f4123d3 100644
--- a/ui-shared.h
+++ b/ui-shared.h
@@ -1,40 +1,41 @@
#ifndef UI_SHARED_H
#define UI_SHARED_H
+extern char *cgit_hosturl();
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 void cgit_index_link(char *name, char *title, char *class,
char *pattern, int ofs);
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, char *grep,
char *pattern);
extern void cgit_commit_link(char *name, char *title, char *class, char *head,
char *rev);
extern void cgit_patch_link(char *name, char *title, char *class, char *head,
char *rev);
extern void cgit_refs_link(char *name, char *title, char *class, char *head,
char *rev, char *path);
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, int local_time);
extern void cgit_print_age(time_t t, time_t max_relative, char *format);
extern void cgit_print_http_headers(struct cgit_context *ctx);
extern void cgit_print_docstart(struct cgit_context *ctx);
extern void cgit_print_docend();
extern void cgit_print_pageheader(struct cgit_context *ctx);
extern void cgit_print_filemode(unsigned short mode);
extern void cgit_print_snapshot_links(const char *repo, const char *head,
const char *hex, int snapshots);
#endif /* UI_SHARED_H */