summaryrefslogtreecommitdiffabout
Side-by-side diff
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--Makefile1
-rw-r--r--cgit.c5
-rw-r--r--cgit.h1
-rw-r--r--configfile.c87
-rw-r--r--configfile.h8
-rw-r--r--parsing.c75
6 files changed, 99 insertions, 78 deletions
diff --git a/Makefile b/Makefile
index 223765d..355186e 100644
--- a/Makefile
+++ b/Makefile
@@ -1,135 +1,136 @@
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.4.1
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-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 clean distclean emptycache force-version get-git
all: cgit git
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)
$(QUIET_CC)$(CC) $(CFLAGS) -o cgit $(OBJECTS) $(EXTLIBS)
$(OBJECTS): git/xdiff/lib.a git/libgit.a
cgit.o: VERSION
-include $(OBJECTS:.o=.d)
git/xdiff/lib.a: | git
git/libgit.a: | git
git:
$(QUIET_SUBDIR0)git $(QUIET_SUBDIR1) xdiff/lib.a
$(QUIET_SUBDIR0)git $(QUIET_SUBDIR1) libgit.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
cd git && $(MAKE) clean
distclean: clean
git clean -d -x
cd git && git clean -d -x
emptycache:
rm -rf $(DESTDIR)$(CACHE_ROOT)/*
get-git:
curl $(GIT_URL) | tar -xj && rm -rf git && mv git-$(GIT_VER) git
diff --git a/cgit.c b/cgit.c
index 73b1f02..1f46e0d 100644
--- a/cgit.c
+++ b/cgit.c
@@ -1,456 +1,457 @@
/* cgit.c: cgi for the git scm
*
* Copyright (C) 2006 Lars Hjemli
*
* Licensed under GNU General Public License v2
* (see COPYING for full license text)
*/
#include "cgit.h"
#include "cache.h"
#include "cmd.h"
+#include "configfile.h"
#include "ui-shared.h"
const char *cgit_version = CGIT_VERSION;
void config_cb(const char *name, const char *value)
{
if (!strcmp(name, "root-title"))
ctx.cfg.root_title = xstrdup(value);
else if (!strcmp(name, "css"))
ctx.cfg.css = xstrdup(value);
else if (!strcmp(name, "logo"))
ctx.cfg.logo = xstrdup(value);
else if (!strcmp(name, "index-header"))
ctx.cfg.index_header = xstrdup(value);
else if (!strcmp(name, "index-info"))
ctx.cfg.index_info = xstrdup(value);
else if (!strcmp(name, "logo-link"))
ctx.cfg.logo_link = xstrdup(value);
else if (!strcmp(name, "module-link"))
ctx.cfg.module_link = xstrdup(value);
else if (!strcmp(name, "virtual-root")) {
ctx.cfg.virtual_root = trim_end(value, '/');
if (!ctx.cfg.virtual_root && (!strcmp(value, "/")))
ctx.cfg.virtual_root = "";
} else if (!strcmp(name, "nocache"))
ctx.cfg.nocache = atoi(value);
else if (!strcmp(name, "snapshots"))
ctx.cfg.snapshots = cgit_parse_snapshots_mask(value);
else if (!strcmp(name, "enable-index-links"))
ctx.cfg.enable_index_links = atoi(value);
else if (!strcmp(name, "enable-log-filecount"))
ctx.cfg.enable_log_filecount = atoi(value);
else if (!strcmp(name, "enable-log-linecount"))
ctx.cfg.enable_log_linecount = atoi(value);
else if (!strcmp(name, "cache-root"))
ctx.cfg.cache_root = xstrdup(value);
else if (!strcmp(name, "cache-root-ttl"))
ctx.cfg.cache_root_ttl = atoi(value);
else if (!strcmp(name, "cache-repo-ttl"))
ctx.cfg.cache_repo_ttl = atoi(value);
else if (!strcmp(name, "cache-static-ttl"))
ctx.cfg.cache_static_ttl = atoi(value);
else if (!strcmp(name, "cache-dynamic-ttl"))
ctx.cfg.cache_dynamic_ttl = atoi(value);
else if (!strcmp(name, "max-message-length"))
ctx.cfg.max_msg_len = atoi(value);
else if (!strcmp(name, "max-repodesc-length"))
ctx.cfg.max_repodesc_len = atoi(value);
else if (!strcmp(name, "max-commit-count"))
ctx.cfg.max_commit_count = atoi(value);
else if (!strcmp(name, "summary-log"))
ctx.cfg.summary_log = atoi(value);
else if (!strcmp(name, "summary-branches"))
ctx.cfg.summary_branches = atoi(value);
else if (!strcmp(name, "summary-tags"))
ctx.cfg.summary_tags = atoi(value);
else if (!strcmp(name, "agefile"))
ctx.cfg.agefile = xstrdup(value);
else if (!strcmp(name, "renamelimit"))
ctx.cfg.renamelimit = atoi(value);
else if (!strcmp(name, "robots"))
ctx.cfg.robots = xstrdup(value);
else if (!strcmp(name, "clone-prefix"))
ctx.cfg.clone_prefix = xstrdup(value);
else if (!strcmp(name, "repo.group"))
ctx.cfg.repo_group = xstrdup(value);
else if (!strcmp(name, "repo.url"))
ctx.repo = cgit_add_repo(value);
else if (!strcmp(name, "repo.name"))
ctx.repo->name = xstrdup(value);
else if (ctx.repo && !strcmp(name, "repo.path"))
ctx.repo->path = trim_end(value, '/');
else if (ctx.repo && !strcmp(name, "repo.clone-url"))
ctx.repo->clone_url = xstrdup(value);
else if (ctx.repo && !strcmp(name, "repo.desc"))
ctx.repo->desc = xstrdup(value);
else if (ctx.repo && !strcmp(name, "repo.owner"))
ctx.repo->owner = xstrdup(value);
else if (ctx.repo && !strcmp(name, "repo.defbranch"))
ctx.repo->defbranch = xstrdup(value);
else if (ctx.repo && !strcmp(name, "repo.snapshots"))
ctx.repo->snapshots = ctx.cfg.snapshots & cgit_parse_snapshots_mask(value); /* XXX: &? */
else if (ctx.repo && !strcmp(name, "repo.enable-log-filecount"))
ctx.repo->enable_log_filecount = ctx.cfg.enable_log_filecount * atoi(value);
else if (ctx.repo && !strcmp(name, "repo.enable-log-linecount"))
ctx.repo->enable_log_linecount = ctx.cfg.enable_log_linecount * atoi(value);
else if (ctx.repo && !strcmp(name, "repo.module-link"))
ctx.repo->module_link= xstrdup(value);
else if (ctx.repo && !strcmp(name, "repo.readme") && value != NULL) {
if (*value == '/')
ctx.repo->readme = xstrdup(value);
else
ctx.repo->readme = xstrdup(fmt("%s/%s", ctx.repo->path, value));
} else if (!strcmp(name, "include"))
- cgit_read_config(value, config_cb);
+ parse_configfile(value, config_cb);
}
static void querystring_cb(const char *name, const char *value)
{
if (!strcmp(name,"r")) {
ctx.qry.repo = xstrdup(value);
ctx.repo = cgit_get_repoinfo(value);
} else if (!strcmp(name, "p")) {
ctx.qry.page = xstrdup(value);
} else if (!strcmp(name, "url")) {
cgit_parse_url(value);
} else if (!strcmp(name, "qt")) {
ctx.qry.grep = xstrdup(value);
} else if (!strcmp(name, "q")) {
ctx.qry.search = xstrdup(value);
} else if (!strcmp(name, "h")) {
ctx.qry.head = xstrdup(value);
ctx.qry.has_symref = 1;
} else if (!strcmp(name, "id")) {
ctx.qry.sha1 = xstrdup(value);
ctx.qry.has_sha1 = 1;
} else if (!strcmp(name, "id2")) {
ctx.qry.sha2 = xstrdup(value);
ctx.qry.has_sha1 = 1;
} else if (!strcmp(name, "ofs")) {
ctx.qry.ofs = atoi(value);
} else if (!strcmp(name, "path")) {
ctx.qry.path = trim_end(value, '/');
} else if (!strcmp(name, "name")) {
ctx.qry.name = xstrdup(value);
}
}
static void prepare_context(struct cgit_context *ctx)
{
memset(ctx, 0, sizeof(ctx));
ctx->cfg.agefile = "info/web/last-modified";
ctx->cfg.cache_dynamic_ttl = 5;
ctx->cfg.cache_max_create_time = 5;
ctx->cfg.cache_repo_ttl = 5;
ctx->cfg.cache_root = CGIT_CACHE_ROOT;
ctx->cfg.cache_root_ttl = 5;
ctx->cfg.cache_static_ttl = -1;
ctx->cfg.css = "/cgit.css";
ctx->cfg.logo = "/git-logo.png";
ctx->cfg.max_commit_count = 50;
ctx->cfg.max_lock_attempts = 5;
ctx->cfg.max_msg_len = 60;
ctx->cfg.max_repodesc_len = 60;
ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s";
ctx->cfg.renamelimit = -1;
ctx->cfg.robots = "index, nofollow";
ctx->cfg.root_title = "Git repository browser";
ctx->cfg.script_name = CGIT_SCRIPT_NAME;
ctx->page.mimetype = "text/html";
ctx->page.charset = PAGE_ENCODING;
ctx->page.filename = NULL;
}
static int cgit_prepare_cache(struct cacheitem *item)
{
if (!ctx.repo && ctx.qry.repo) {
ctx.page.title = fmt("%s - %s", ctx.cfg.root_title,
"Bad request");
cgit_print_http_headers(&ctx);
cgit_print_docstart(&ctx);
cgit_print_pageheader(&ctx);
cgit_print_error(fmt("Unknown repo: %s", ctx.qry.repo));
cgit_print_docend();
return 0;
}
if (!ctx.repo) {
item->name = xstrdup(fmt("%s/index.html", ctx.cfg.cache_root));
item->ttl = ctx.cfg.cache_root_ttl;
return 1;
}
if (!ctx.qry.page) {
item->name = xstrdup(fmt("%s/%s/index.%s.html", ctx.cfg.cache_root,
cache_safe_filename(ctx.repo->url),
cache_safe_filename(ctx.qry.raw)));
item->ttl = ctx.cfg.cache_repo_ttl;
} else {
item->name = xstrdup(fmt("%s/%s/%s/%s.html", ctx.cfg.cache_root,
cache_safe_filename(ctx.repo->url),
ctx.qry.page,
cache_safe_filename(ctx.qry.raw)));
if (ctx.qry.has_symref)
item->ttl = ctx.cfg.cache_dynamic_ttl;
else if (ctx.qry.has_sha1)
item->ttl = ctx.cfg.cache_static_ttl;
else
item->ttl = ctx.cfg.cache_repo_ttl;
}
return 1;
}
struct refmatch {
char *req_ref;
char *first_ref;
int match;
};
int find_current_ref(const char *refname, const unsigned char *sha1,
int flags, void *cb_data)
{
struct refmatch *info;
info = (struct refmatch *)cb_data;
if (!strcmp(refname, info->req_ref))
info->match = 1;
if (!info->first_ref)
info->first_ref = xstrdup(refname);
return info->match;
}
char *find_default_branch(struct cgit_repo *repo)
{
struct refmatch info;
info.req_ref = repo->defbranch;
info.first_ref = NULL;
info.match = 0;
for_each_branch_ref(find_current_ref, &info);
if (info.match)
return info.req_ref;
else
return info.first_ref;
}
static int prepare_repo_cmd(struct cgit_context *ctx)
{
char *tmp;
unsigned char sha1[20];
int nongit = 0;
setenv("GIT_DIR", ctx->repo->path, 1);
setup_git_directory_gently(&nongit);
if (nongit) {
ctx->page.title = fmt("%s - %s", ctx->cfg.root_title,
"config error");
tmp = fmt("Not a git repository: '%s'", ctx->repo->path);
ctx->repo = NULL;
cgit_print_http_headers(ctx);
cgit_print_docstart(ctx);
cgit_print_pageheader(ctx);
cgit_print_error(tmp);
cgit_print_docend();
return 1;
}
ctx->page.title = fmt("%s - %s", ctx->repo->name, ctx->repo->desc);
if (!ctx->qry.head) {
ctx->qry.head = xstrdup(find_default_branch(ctx->repo));
ctx->repo->defbranch = ctx->qry.head;
}
if (!ctx->qry.head) {
cgit_print_http_headers(ctx);
cgit_print_docstart(ctx);
cgit_print_pageheader(ctx);
cgit_print_error("Repository seems to be empty");
cgit_print_docend();
return 1;
}
if (get_sha1(ctx->qry.head, sha1)) {
tmp = xstrdup(ctx->qry.head);
ctx->qry.head = ctx->repo->defbranch;
cgit_print_http_headers(ctx);
cgit_print_docstart(ctx);
cgit_print_pageheader(ctx);
cgit_print_error(fmt("Invalid branch: %s", tmp));
cgit_print_docend();
return 1;
}
return 0;
}
static void process_request(struct cgit_context *ctx)
{
struct cgit_cmd *cmd;
cmd = cgit_get_cmd(ctx);
if (!cmd) {
ctx->page.title = "cgit error";
ctx->repo = NULL;
cgit_print_http_headers(ctx);
cgit_print_docstart(ctx);
cgit_print_pageheader(ctx);
cgit_print_error("Invalid request");
cgit_print_docend();
return;
}
if (cmd->want_repo && prepare_repo_cmd(ctx))
return;
if (cmd->want_layout) {
cgit_print_http_headers(ctx);
cgit_print_docstart(ctx);
cgit_print_pageheader(ctx);
}
cmd->fn(ctx);
if (cmd->want_layout)
cgit_print_docend();
}
static long ttl_seconds(long ttl)
{
if (ttl<0)
return 60 * 60 * 24 * 365;
else
return ttl * 60;
}
static void cgit_fill_cache(struct cacheitem *item, int use_cache)
{
int stdout2;
if (use_cache) {
stdout2 = chk_positive(dup(STDOUT_FILENO),
"Preserving STDOUT");
chk_zero(close(STDOUT_FILENO), "Closing STDOUT");
chk_positive(dup2(item->fd, STDOUT_FILENO), "Dup2(cachefile)");
}
ctx.page.modified = time(NULL);
ctx.page.expires = ctx.page.modified + ttl_seconds(item->ttl);
process_request(&ctx);
if (use_cache) {
chk_zero(close(STDOUT_FILENO), "Close redirected STDOUT");
chk_positive(dup2(stdout2, STDOUT_FILENO),
"Restoring original STDOUT");
chk_zero(close(stdout2), "Closing temporary STDOUT");
}
}
static void cgit_check_cache(struct cacheitem *item)
{
int i = 0;
top:
if (++i > ctx.cfg.max_lock_attempts) {
die("cgit_refresh_cache: unable to lock %s: %s",
item->name, strerror(errno));
}
if (!cache_exist(item)) {
if (!cache_lock(item)) {
sleep(1);
goto top;
}
if (!cache_exist(item)) {
cgit_fill_cache(item, 1);
cache_unlock(item);
} else {
cache_cancel_lock(item);
}
} else if (cache_expired(item) && cache_lock(item)) {
if (cache_expired(item)) {
cgit_fill_cache(item, 1);
cache_unlock(item);
} else {
cache_cancel_lock(item);
}
}
}
static void cgit_print_cache(struct cacheitem *item)
{
static char buf[4096];
ssize_t i;
int fd = open(item->name, O_RDONLY);
if (fd<0)
die("Unable to open cached file %s", item->name);
while((i=read(fd, buf, sizeof(buf))) > 0)
write(STDOUT_FILENO, buf, i);
close(fd);
}
static void cgit_parse_args(int argc, const char **argv)
{
int i;
for (i = 1; i < argc; i++) {
if (!strncmp(argv[i], "--cache=", 8)) {
ctx.cfg.cache_root = xstrdup(argv[i]+8);
}
if (!strcmp(argv[i], "--nocache")) {
ctx.cfg.nocache = 1;
}
if (!strncmp(argv[i], "--query=", 8)) {
ctx.qry.raw = xstrdup(argv[i]+8);
}
if (!strncmp(argv[i], "--repo=", 7)) {
ctx.qry.repo = xstrdup(argv[i]+7);
}
if (!strncmp(argv[i], "--page=", 7)) {
ctx.qry.page = xstrdup(argv[i]+7);
}
if (!strncmp(argv[i], "--head=", 7)) {
ctx.qry.head = xstrdup(argv[i]+7);
ctx.qry.has_symref = 1;
}
if (!strncmp(argv[i], "--sha1=", 7)) {
ctx.qry.sha1 = xstrdup(argv[i]+7);
ctx.qry.has_sha1 = 1;
}
if (!strncmp(argv[i], "--ofs=", 6)) {
ctx.qry.ofs = atoi(argv[i]+6);
}
}
}
int main(int argc, const char **argv)
{
struct cacheitem item;
const char *cgit_config_env = getenv("CGIT_CONFIG");
prepare_context(&ctx);
item.st.st_mtime = time(NULL);
cgit_repolist.length = 0;
cgit_repolist.count = 0;
cgit_repolist.repos = NULL;
- cgit_read_config(cgit_config_env ? cgit_config_env : CGIT_CONFIG,
+ parse_configfile(cgit_config_env ? cgit_config_env : CGIT_CONFIG,
config_cb);
if (getenv("SCRIPT_NAME"))
ctx.cfg.script_name = xstrdup(getenv("SCRIPT_NAME"));
if (getenv("QUERY_STRING"))
ctx.qry.raw = xstrdup(getenv("QUERY_STRING"));
cgit_parse_args(argc, argv);
cgit_parse_query(ctx.qry.raw, querystring_cb);
if (!cgit_prepare_cache(&item))
return 0;
if (ctx.cfg.nocache) {
cgit_fill_cache(&item, 0);
} else {
cgit_check_cache(&item);
cgit_print_cache(&item);
}
return 0;
}
diff --git a/cgit.h b/cgit.h
index f600912..91d18f8 100644
--- a/cgit.h
+++ b/cgit.h
@@ -1,227 +1,226 @@
#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"
#define FMT_SHORTDATE "%Y-%m-%d"
/*
* 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;
int ofs;
};
struct cgit_config {
char *agefile;
char *cache_root;
char *clone_prefix;
char *css;
char *index_header;
char *index_info;
char *logo;
char *logo_link;
char *module_link;
char *repo_group;
char *robots;
char *root_title;
char *script_name;
char *virtual_root;
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 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 int hextoint(char c);
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 int cgit_read_config(const char *filename, configfn fn);
extern int cgit_parse_query(char *txt, configfn fn);
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);
#endif /* CGIT_H */
diff --git a/configfile.c b/configfile.c
new file mode 100644
index 0000000..4908058
--- a/dev/null
+++ b/configfile.c
@@ -0,0 +1,87 @@
+/* configfile.c: parsing of config files
+ *
+ * Copyright (C) 2008 Lars Hjemli
+ *
+ * Licensed under GNU General Public License v2
+ * (see COPYING for full license text)
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include "configfile.h"
+
+int next_char(FILE *f)
+{
+ int c = fgetc(f);
+ if (c=='\r') {
+ c = fgetc(f);
+ if (c!='\n') {
+ ungetc(c, f);
+ c = '\r';
+ }
+ }
+ return c;
+}
+
+void skip_line(FILE *f)
+{
+ int c;
+
+ while((c=next_char(f)) && c!='\n' && c!=EOF)
+ ;
+}
+
+int read_config_line(FILE *f, char *line, const char **value, int bufsize)
+{
+ int i = 0, isname = 0;
+
+ *value = NULL;
+ while(i<bufsize-1) {
+ int c = next_char(f);
+ if (!isname && (c=='#' || c==';')) {
+ skip_line(f);
+ continue;
+ }
+ if (!isname && isspace(c))
+ continue;
+
+ if (c=='=' && !*value) {
+ line[i] = 0;
+ *value = &line[i+1];
+ } else if (c=='\n' && !isname) {
+ i = 0;
+ continue;
+ } else if (c=='\n' || c==EOF) {
+ line[i] = 0;
+ break;
+ } else {
+ line[i]=c;
+ }
+ isname = 1;
+ i++;
+ }
+ line[i+1] = 0;
+ return i;
+}
+
+int parse_configfile(const char *filename, configfile_value_fn fn)
+{
+ static int nesting;
+ int len;
+ char line[256];
+ const char *value;
+ FILE *f;
+
+ /* cancel deeply nested include-commands */
+ if (nesting > 8)
+ return -1;
+ if (!(f = fopen(filename, "r")))
+ return -1;
+ nesting++;
+ while((len = read_config_line(f, line, &value, sizeof(line))) > 0)
+ fn(line, value);
+ nesting--;
+ fclose(f);
+ return 0;
+}
+
diff --git a/configfile.h b/configfile.h
new file mode 100644
index 0000000..04235e5
--- a/dev/null
+++ b/configfile.h
@@ -0,0 +1,8 @@
+#ifndef CONFIGFILE_H
+#define CONFIGFILE_H
+
+typedef void (*configfile_value_fn)(const char *name, const char *value);
+
+extern int parse_configfile(const char *filename, configfile_value_fn fn);
+
+#endif /* CONFIGFILE_H */
diff --git a/parsing.c b/parsing.c
index 8dce488..9a4a7a3 100644
--- a/parsing.c
+++ b/parsing.c
@@ -1,332 +1,257 @@
/* config.c: parsing of config files
*
* Copyright (C) 2006 Lars Hjemli
*
* Licensed under GNU General Public License v2
* (see COPYING for full license text)
*/
#include "cgit.h"
-int next_char(FILE *f)
-{
- int c = fgetc(f);
- if (c=='\r') {
- c = fgetc(f);
- if (c!='\n') {
- ungetc(c, f);
- c = '\r';
- }
- }
- return c;
-}
-
-void skip_line(FILE *f)
-{
- int c;
-
- while((c=next_char(f)) && c!='\n' && c!=EOF)
- ;
-}
-
-int read_config_line(FILE *f, char *line, const char **value, int bufsize)
-{
- int i = 0, isname = 0;
-
- *value = NULL;
- while(i<bufsize-1) {
- int c = next_char(f);
- if (!isname && (c=='#' || c==';')) {
- skip_line(f);
- continue;
- }
- if (!isname && isspace(c))
- continue;
-
- if (c=='=' && !*value) {
- line[i] = 0;
- *value = &line[i+1];
- } else if (c=='\n' && !isname) {
- i = 0;
- continue;
- } else if (c=='\n' || c==EOF) {
- line[i] = 0;
- break;
- } else {
- line[i]=c;
- }
- isname = 1;
- i++;
- }
- line[i+1] = 0;
- return i;
-}
-
-int cgit_read_config(const char *filename, configfn fn)
-{
- static int nesting;
- int len;
- char line[256];
- const char *value;
- FILE *f;
-
- /* cancel deeply nested include-commands */
- if (nesting > 8)
- return -1;
- if (!(f = fopen(filename, "r")))
- return -1;
- nesting++;
- while((len = read_config_line(f, line, &value, sizeof(line))) > 0)
- (*fn)(line, value);
- nesting--;
- fclose(f);
- return 0;
-}
-
char *convert_query_hexchar(char *txt)
{
int d1, d2;
if (strlen(txt) < 3) {
*txt = '\0';
return txt-1;
}
d1 = hextoint(*(txt+1));
d2 = hextoint(*(txt+2));
if (d1<0 || d2<0) {
strcpy(txt, txt+3);
return txt-1;
} else {
*txt = d1 * 16 + d2;
strcpy(txt+1, txt+3);
return txt;
}
}
int cgit_parse_query(char *txt, configfn fn)
{
char *t, *value = NULL, c;
if (!txt)
return 0;
t = txt = xstrdup(txt);
while((c=*t) != '\0') {
if (c=='=') {
*t = '\0';
value = t+1;
} else if (c=='+') {
*t = ' ';
} else if (c=='%') {
t = convert_query_hexchar(t);
} else if (c=='&') {
*t = '\0';
(*fn)(txt, value);
txt = t+1;
value = NULL;
}
t++;
}
if (t!=txt)
(*fn)(txt, value);
return 0;
}
/*
* url syntax: [repo ['/' cmd [ '/' path]]]
* repo: any valid repo url, may contain '/'
* cmd: log | commit | diff | tree | view | blob | snapshot
* path: any valid path, may contain '/'
*
*/
void cgit_parse_url(const char *url)
{
char *cmd, *p;
ctx.repo = NULL;
if (!url || url[0] == '\0')
return;
ctx.repo = cgit_get_repoinfo(url);
if (ctx.repo) {
ctx.qry.repo = ctx.repo->url;
return;
}
cmd = strchr(url, '/');
while (!ctx.repo && cmd) {
cmd[0] = '\0';
ctx.repo = cgit_get_repoinfo(url);
if (ctx.repo == NULL) {
cmd[0] = '/';
cmd = strchr(cmd + 1, '/');
continue;
}
ctx.qry.repo = ctx.repo->url;
p = strchr(cmd + 1, '/');
if (p) {
p[0] = '\0';
if (p[1])
ctx.qry.path = trim_end(p + 1, '/');
}
if (cmd[1])
ctx.qry.page = xstrdup(cmd + 1);
return;
}
}
char *substr(const char *head, const char *tail)
{
char *buf;
buf = xmalloc(tail - head + 1);
strncpy(buf, head, tail - head);
buf[tail - head] = '\0';
return buf;
}
struct commitinfo *cgit_parse_commit(struct commit *commit)
{
struct commitinfo *ret;
char *p = commit->buffer, *t = commit->buffer;
ret = xmalloc(sizeof(*ret));
ret->commit = commit;
ret->author = NULL;
ret->author_email = NULL;
ret->committer = NULL;
ret->committer_email = NULL;
ret->subject = NULL;
ret->msg = NULL;
ret->msg_encoding = NULL;
if (p == NULL)
return ret;
if (strncmp(p, "tree ", 5))
die("Bad commit: %s", sha1_to_hex(commit->object.sha1));
else
p += 46; // "tree " + hex[40] + "\n"
while (!strncmp(p, "parent ", 7))
p += 48; // "parent " + hex[40] + "\n"
if (!strncmp(p, "author ", 7)) {
p += 7;
t = strchr(p, '<') - 1;
ret->author = substr(p, t);
p = t;
t = strchr(t, '>') + 1;
ret->author_email = substr(p, t);
ret->author_date = atol(t+1);
p = strchr(t, '\n') + 1;
}
if (!strncmp(p, "committer ", 9)) {
p += 9;
t = strchr(p, '<') - 1;
ret->committer = substr(p, t);
p = t;
t = strchr(t, '>') + 1;
ret->committer_email = substr(p, t);
ret->committer_date = atol(t+1);
p = strchr(t, '\n') + 1;
}
if (!strncmp(p, "encoding ", 9)) {
p += 9;
t = strchr(p, '\n') + 1;
ret->msg_encoding = substr(p, t);
p = t;
} else
ret->msg_encoding = xstrdup(PAGE_ENCODING);
while (*p && (*p != '\n'))
p = strchr(p, '\n') + 1; // skip unknown header fields
while (*p == '\n')
p = strchr(p, '\n') + 1;
t = strchr(p, '\n');
if (t) {
if (*t == '\0')
ret->subject = "** empty **";
else
ret->subject = substr(p, t);
p = t + 1;
while (*p == '\n')
p = strchr(p, '\n') + 1;
ret->msg = xstrdup(p);
} else
ret->subject = substr(p, p+strlen(p));
if(strcmp(ret->msg_encoding, PAGE_ENCODING)) {
t = reencode_string(ret->subject, PAGE_ENCODING,
ret->msg_encoding);
if(t) {
free(ret->subject);
ret->subject = t;
}
t = reencode_string(ret->msg, PAGE_ENCODING,
ret->msg_encoding);
if(t) {
free(ret->msg);
ret->msg = t;
}
}
return ret;
}
struct taginfo *cgit_parse_tag(struct tag *tag)
{
void *data;
enum object_type type;
unsigned long size;
char *p, *t;
struct taginfo *ret;
data = read_sha1_file(tag->object.sha1, &type, &size);
if (!data || type != OBJ_TAG) {
free(data);
return 0;
}
ret = xmalloc(sizeof(*ret));
ret->tagger = NULL;
ret->tagger_email = NULL;
ret->tagger_date = 0;
ret->msg = NULL;
p = data;
while (p && *p) {
if (*p == '\n')
break;
if (!strncmp(p, "tagger ", 7)) {
p += 7;
t = strchr(p, '<') - 1;
ret->tagger = substr(p, t);
p = t;
t = strchr(t, '>') + 1;
ret->tagger_email = substr(p, t);
ret->tagger_date = atol(t+1);
}
p = strchr(p, '\n') + 1;
}
while (p && *p && (*p != '\n'))
p = strchr(p, '\n') + 1; // skip unknown tag fields
while (p && (*p == '\n'))
p = strchr(p, '\n') + 1;
if (p && *p)
ret->msg = xstrdup(p);
free(data);
return ret;
}