X-Git-Url: https://git.cameronkatri.com/cgit.git/blobdiff_plain/d1f3bbe9d22029f45a77bb938c176ccc0c827d46..4046e8ef66225928a4f0d2cd71479e401faf7c3b:/shared.c?ds=sidebyside diff --git a/shared.c b/shared.c index 808e674..8ed14c0 100644 --- a/shared.c +++ b/shared.c @@ -1,78 +1,40 @@ /* shared.c: global vars + some callback functions * - * Copyright (C) 2006 Lars Hjemli + * Copyright (C) 2006-2014 cgit Development Team * * Licensed under GNU General Public License v2 * (see COPYING for full license text) */ #include "cgit.h" +#include struct cgit_repolist cgit_repolist; struct cgit_context ctx; -int cgit_cmd; - -const char *cgit_version = CGIT_VERSION; - -int htmlfd = 0; - -void cgit_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; -} - -int cgit_get_cmd_index(const char *cmd) -{ - static char *cmds[] = {"log", "commit", "diff", "tree", "blob", - "snapshot", "tag", "refs", "patch", NULL}; - int i; - - for(i = 0; cmds[i]; i++) - if (!strcmp(cmd, cmds[i])) - return i + 1; - return 0; -} int chk_zero(int result, char *msg) { if (result != 0) - die("%s: %s", msg, strerror(errno)); + die_errno("%s", msg); return result; } int chk_positive(int result, char *msg) { if (result <= 0) - die("%s: %s", msg, strerror(errno)); + die_errno("%s", msg); return result; } int chk_non_negative(int result, char *msg) { - if (result < 0) - die("%s: %s",msg, strerror(errno)); + if (result < 0) + die_errno("%s", msg); return result; } -struct cgit_repo *add_repo(const char *url) +char *cgit_default_repo_desc = "[no description]"; +struct cgit_repo *cgit_add_repo(const char *url) { struct cgit_repo *ret; @@ -87,18 +49,31 @@ struct cgit_repo *add_repo(const char *url) } ret = &cgit_repolist.repos[cgit_repolist.count-1]; + memset(ret, 0, sizeof(struct cgit_repo)); ret->url = trim_end(url, '/'); ret->name = ret->url; ret->path = NULL; - ret->desc = "[no description]"; + ret->desc = cgit_default_repo_desc; ret->owner = NULL; - ret->group = ctx.cfg.repo_group; - ret->defbranch = "master"; + ret->section = ctx.cfg.section; ret->snapshots = ctx.cfg.snapshots; + ret->enable_commit_graph = ctx.cfg.enable_commit_graph; ret->enable_log_filecount = ctx.cfg.enable_log_filecount; ret->enable_log_linecount = ctx.cfg.enable_log_linecount; + ret->enable_remote_branches = ctx.cfg.enable_remote_branches; + ret->enable_subject_links = ctx.cfg.enable_subject_links; + ret->max_stats = ctx.cfg.max_stats; + ret->branch_sort = ctx.cfg.branch_sort; + ret->commit_sort = ctx.cfg.commit_sort; ret->module_link = ctx.cfg.module_link; - ret->readme = NULL; + ret->readme = ctx.cfg.readme; + ret->mtime = -1; + ret->about_filter = ctx.cfg.about_filter; + ret->commit_filter = ctx.cfg.commit_filter; + ret->source_filter = ctx.cfg.source_filter; + ret->email_filter = ctx.cfg.email_filter; + ret->clone_url = ctx.cfg.clone_url; + ret->submodules.strdup_strings = 1; return ret; } @@ -107,7 +82,7 @@ struct cgit_repo *cgit_get_repoinfo(const char *url) int i; struct cgit_repo *repo; - for (i=0; iurl, url)) return repo; @@ -115,131 +90,6 @@ struct cgit_repo *cgit_get_repoinfo(const char *url) return NULL; } -void cgit_global_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 = 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, cgit_global_config_cb); -} - -void cgit_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); - cgit_cmd = cgit_get_cmd_index(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); - } -} - void *cgit_free_commitinfo(struct commitinfo *info) { free(info->author); @@ -253,38 +103,39 @@ void *cgit_free_commitinfo(struct commitinfo *info) return NULL; } -int hextoint(char c) -{ - if (c >= 'a' && c <= 'f') - return 10 + c - 'a'; - else if (c >= 'A' && c <= 'F') - return 10 + c - 'A'; - else if (c >= '0' && c <= '9') - return c - '0'; - else - 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 = strlen(str); + while (len > 0 && str[len - 1] == c) len--; - if (len == 0) return NULL; + return xstrndup(str, len); +} - c = t[len]; - t[len] = '\0'; - s = xstrdup(t); - t[len] = c; - return s; +char *ensure_end(const char *str, char c) +{ + size_t len = strlen(str); + char *result; + + if (len && str[len - 1] == c) + return xstrndup(str, len); + + result = xmalloc(len + 2); + memcpy(result, str, len); + result[len] = '/'; + result[len + 1] = '\0'; + return result; +} + +void strbuf_ensure_end(struct strbuf *sb, char c) +{ + if (!sb->len || sb->buf[sb->len - 1] != c) + strbuf_addch(sb, c); } char *strlpart(char *txt, int maxlen) @@ -330,7 +181,7 @@ void cgit_add_ref(struct reflist *list, struct refinfo *ref) list->refs[list->count++] = ref; } -struct refinfo *cgit_mk_refinfo(const char *refname, const unsigned char *sha1) +static struct refinfo *cgit_mk_refinfo(const char *refname, const unsigned char *sha1) { struct refinfo *ref; @@ -348,6 +199,42 @@ struct refinfo *cgit_mk_refinfo(const char *refname, const unsigned char *sha1) return ref; } +static void cgit_free_taginfo(struct taginfo *tag) +{ + if (tag->tagger) + free(tag->tagger); + if (tag->tagger_email) + free(tag->tagger_email); + if (tag->msg) + free(tag->msg); + free(tag); +} + +static void cgit_free_refinfo(struct refinfo *ref) +{ + if (ref->refname) + free((char *)ref->refname); + switch (ref->object->type) { + case OBJ_TAG: + cgit_free_taginfo(ref->tag); + break; + case OBJ_COMMIT: + cgit_free_commitinfo(ref->commit); + break; + } + free(ref); +} + +void cgit_free_reflist_inner(struct reflist *list) +{ + int i; + + for (i = 0; i < list->count; i++) { + cgit_free_refinfo(list->refs[i]); + } + free(list->refs); +} + int cgit_refs_cb(const char *refname, const unsigned char *sha1, int flags, void *cb_data) { @@ -359,8 +246,8 @@ int cgit_refs_cb(const char *refname, const unsigned char *sha1, int flags, return 0; } -void cgit_diff_tree_cb(struct diff_queue_struct *q, - struct diff_options *options, void *data) +static void cgit_diff_tree_cb(struct diff_queue_struct *q, + struct diff_options *options, void *data) { int i; @@ -379,7 +266,7 @@ static int load_mmfile(mmfile_t *file, const unsigned char *sha1) file->ptr = (char *)""; file->size = 0; } else { - file->ptr = read_sha1_file(sha1, &type, + file->ptr = read_sha1_file(sha1, &type, (unsigned long *)&file->size); } return 1; @@ -396,7 +283,7 @@ static int load_mmfile(mmfile_t *file, const unsigned char *sha1) char *diffbuf = NULL; int buflen = 0; -int filediff_cb(void *priv, mmbuffer_t *mb, int nbuf) +static int filediff_cb(void *priv, mmbuffer_t *mb, int nbuf) { int i; @@ -431,8 +318,9 @@ int filediff_cb(void *priv, mmbuffer_t *mb, int nbuf) } int cgit_diff_files(const unsigned char *old_sha1, - const unsigned char *new_sha1, - linediff_fn fn) + const unsigned char *new_sha1, unsigned long *old_size, + unsigned long *new_size, int *binary, int context, + int ignorews, linediff_fn fn) { mmfile_t file1, file2; xpparam_t diff_params; @@ -442,52 +330,230 @@ int cgit_diff_files(const unsigned char *old_sha1, if (!load_mmfile(&file1, old_sha1) || !load_mmfile(&file2, new_sha1)) return 1; + *old_size = file1.size; + *new_size = file2.size; + + if ((file1.ptr && buffer_is_binary(file1.ptr, file1.size)) || + (file2.ptr && buffer_is_binary(file2.ptr, file2.size))) { + *binary = 1; + if (file1.size) + free(file1.ptr); + if (file2.size) + free(file2.ptr); + return 0; + } + + memset(&diff_params, 0, sizeof(diff_params)); + memset(&emit_params, 0, sizeof(emit_params)); + memset(&emit_cb, 0, sizeof(emit_cb)); diff_params.flags = XDF_NEED_MINIMAL; - emit_params.ctxlen = 3; + if (ignorews) + diff_params.flags |= XDF_IGNORE_WHITESPACE; + emit_params.ctxlen = context > 0 ? context : 3; emit_params.flags = XDL_EMIT_FUNCNAMES; - emit_params.find_func = NULL; emit_cb.outf = filediff_cb; emit_cb.priv = fn; xdl_diff(&file1, &file2, &diff_params, &emit_params, &emit_cb); + if (file1.size) + free(file1.ptr); + if (file2.size) + free(file2.ptr); return 0; } void cgit_diff_tree(const unsigned char *old_sha1, const unsigned char *new_sha1, - filepair_fn fn, const char *prefix) + filepair_fn fn, const char *prefix, int ignorews) { struct diff_options opt; - int ret; - int prefixlen; + struct pathspec_item item; + memset(&item, 0, sizeof(item)); diff_setup(&opt); opt.output_format = DIFF_FORMAT_CALLBACK; opt.detect_rename = 1; opt.rename_limit = ctx.cfg.renamelimit; DIFF_OPT_SET(&opt, RECURSIVE); + if (ignorews) + DIFF_XDL_SET(&opt, IGNORE_WHITESPACE); opt.format_callback = cgit_diff_tree_cb; opt.format_callback_data = fn; if (prefix) { - opt.nr_paths = 1; - opt.paths = &prefix; - prefixlen = strlen(prefix); - opt.pathlens = &prefixlen; + item.match = prefix; + item.len = strlen(prefix); + opt.pathspec.nr = 1; + opt.pathspec.items = &item; } diff_setup_done(&opt); if (old_sha1 && !is_null_sha1(old_sha1)) - ret = diff_tree_sha1(old_sha1, new_sha1, "", &opt); + diff_tree_sha1(old_sha1, new_sha1, "", &opt); else - ret = diff_root_tree_sha1(new_sha1, "", &opt); + diff_root_tree_sha1(new_sha1, "", &opt); diffcore_std(&opt); diff_flush(&opt); } -void cgit_diff_commit(struct commit *commit, filepair_fn fn) +void cgit_diff_commit(struct commit *commit, filepair_fn fn, const char *prefix) { unsigned char *old_sha1 = NULL; if (commit->parents) old_sha1 = commit->parents->item->object.sha1; - cgit_diff_tree(old_sha1, commit->object.sha1, fn, NULL); + cgit_diff_tree(old_sha1, commit->object.sha1, fn, prefix, + ctx.qry.ignorews); +} + +int cgit_parse_snapshots_mask(const char *str) +{ + struct string_list tokens = STRING_LIST_INIT_DUP; + struct string_list_item *item; + const struct cgit_snapshot_format *f; + int rv = 0; + + /* favor legacy setting */ + if (atoi(str)) + return 1; + + string_list_split(&tokens, str, ' ', -1); + string_list_remove_empty_items(&tokens, 0); + + for_each_string_list_item(item, &tokens) { + for (f = cgit_snapshot_formats; f->suffix; f++) { + if (!strcmp(item->string, f->suffix) || + !strcmp(item->string, f->suffix + 1)) { + rv |= f->bit; + break; + } + } + } + + string_list_clear(&tokens, 0); + return rv; +} + +typedef struct { + char * name; + char * value; +} cgit_env_var; + +void cgit_prepare_repo_env(struct cgit_repo * repo) +{ + cgit_env_var env_vars[] = { + { .name = "CGIT_REPO_URL", .value = repo->url }, + { .name = "CGIT_REPO_NAME", .value = repo->name }, + { .name = "CGIT_REPO_PATH", .value = repo->path }, + { .name = "CGIT_REPO_OWNER", .value = repo->owner }, + { .name = "CGIT_REPO_DEFBRANCH", .value = repo->defbranch }, + { .name = "CGIT_REPO_SECTION", .value = repo->section }, + { .name = "CGIT_REPO_CLONE_URL", .value = repo->clone_url } + }; + int env_var_count = ARRAY_SIZE(env_vars); + cgit_env_var *p, *q; + static char *warn = "cgit warning: failed to set env: %s=%s\n"; + + p = env_vars; + q = p + env_var_count; + for (; p < q; p++) + if (p->value && setenv(p->name, p->value, 1)) + fprintf(stderr, warn, p->name, p->value); +} + +/* Read the content of the specified file into a newly allocated buffer, + * zeroterminate the buffer and return 0 on success, errno otherwise. + */ +int readfile(const char *path, char **buf, size_t *size) +{ + int fd, e; + struct stat st; + + fd = open(path, O_RDONLY); + if (fd == -1) + return errno; + if (fstat(fd, &st)) { + e = errno; + close(fd); + return e; + } + if (!S_ISREG(st.st_mode)) { + close(fd); + return EISDIR; + } + *buf = xmalloc(st.st_size + 1); + *size = read_in_full(fd, *buf, st.st_size); + e = errno; + (*buf)[*size] = '\0'; + close(fd); + return (*size == st.st_size ? 0 : e); +} + +static int is_token_char(char c) +{ + return isalnum(c) || c == '_'; +} + +/* Replace name with getenv(name), return pointer to zero-terminating char + */ +static char *expand_macro(char *name, int maxlength) +{ + char *value; + int len; + + len = 0; + value = getenv(name); + if (value) { + len = strlen(value); + if (len > maxlength) + len = maxlength; + strncpy(name, value, len); + } + return name + len; +} + +#define EXPBUFSIZE (1024 * 8) + +/* Replace all tokens prefixed by '$' in the specified text with the + * value of the named environment variable. + * NB: the return value is a static buffer, i.e. it must be strdup'd + * by the caller. + */ +char *expand_macros(const char *txt) +{ + static char result[EXPBUFSIZE]; + char *p, *start; + int len; + + p = result; + start = NULL; + while (p < result + EXPBUFSIZE - 1 && txt && *txt) { + *p = *txt; + if (start) { + if (!is_token_char(*txt)) { + if (p - start > 0) { + *p = '\0'; + len = result + EXPBUFSIZE - start - 1; + p = expand_macro(start, len) - 1; + } + start = NULL; + txt--; + } + p++; + txt++; + continue; + } + if (*txt == '$') { + start = p; + txt++; + continue; + } + p++; + txt++; + } + *p = '\0'; + if (start && p - start > 0) { + len = result + EXPBUFSIZE - start - 1; + p = expand_macro(start, len); + *p = '\0'; + } + return result; }