+ memset(ctx, 0, sizeof(*ctx));
+ ctx->cfg.agefile = "info/web/last-modified";
+ ctx->cfg.nocache = 0;
+ ctx->cfg.cache_size = 0;
+ ctx->cfg.cache_max_create_time = 5;
+ ctx->cfg.cache_root = CGIT_CACHE_ROOT;
+ ctx->cfg.cache_about_ttl = 15;
+ ctx->cfg.cache_repo_ttl = 5;
+ ctx->cfg.cache_root_ttl = 5;
+ ctx->cfg.cache_scanrc_ttl = 15;
+ ctx->cfg.cache_dynamic_ttl = 5;
+ ctx->cfg.cache_static_ttl = -1;
+ ctx->cfg.case_sensitive_sort = 1;
+ ctx->cfg.branch_sort = 0;
+ ctx->cfg.commit_sort = 0;
+ ctx->cfg.css = "/cgit.css";
+ ctx->cfg.logo = "/cgit.png";
+ ctx->cfg.favicon = "/favicon.ico";
+ ctx->cfg.local_time = 0;
+ ctx->cfg.enable_http_clone = 1;
+ ctx->cfg.enable_index_owner = 1;
+ ctx->cfg.enable_tree_linenumbers = 1;
+ ctx->cfg.enable_git_config = 0;
+ ctx->cfg.max_repo_count = 50;
+ ctx->cfg.max_commit_count = 50;
+ ctx->cfg.max_lock_attempts = 5;
+ ctx->cfg.max_msg_len = 80;
+ ctx->cfg.max_repodesc_len = 80;
+ ctx->cfg.max_blob_size = 0;
+ ctx->cfg.max_stats = 0;
+ ctx->cfg.project_list = NULL;
+ ctx->cfg.renamelimit = -1;
+ ctx->cfg.remove_suffix = 0;
+ ctx->cfg.robots = "index, nofollow";
+ ctx->cfg.root_title = "Git repository browser";
+ ctx->cfg.root_desc = "a fast webinterface for the git dscm";
+ ctx->cfg.scan_hidden_path = 0;
+ ctx->cfg.script_name = CGIT_SCRIPT_NAME;
+ ctx->cfg.section = "";
+ ctx->cfg.repository_sort = "name";
+ ctx->cfg.section_sort = 1;
+ ctx->cfg.summary_branches = 10;
+ ctx->cfg.summary_log = 10;
+ ctx->cfg.summary_tags = 10;
+ ctx->cfg.max_atom_items = 10;
+ ctx->cfg.ssdiff = 0;
+ ctx->env.cgit_config = getenv("CGIT_CONFIG");
+ ctx->env.http_host = getenv("HTTP_HOST");
+ ctx->env.https = getenv("HTTPS");
+ ctx->env.no_http = getenv("NO_HTTP");
+ ctx->env.path_info = getenv("PATH_INFO");
+ ctx->env.query_string = getenv("QUERY_STRING");
+ ctx->env.request_method = getenv("REQUEST_METHOD");
+ ctx->env.script_name = getenv("SCRIPT_NAME");
+ ctx->env.server_name = getenv("SERVER_NAME");
+ ctx->env.server_port = getenv("SERVER_PORT");
+ ctx->page.mimetype = "text/html";
+ ctx->page.charset = PAGE_ENCODING;
+ ctx->page.filename = NULL;
+ ctx->page.size = 0;
+ ctx->page.modified = time(NULL);
+ ctx->page.expires = ctx->page.modified;
+ ctx->page.etag = NULL;
+ memset(&ctx->cfg.mimetypes, 0, sizeof(struct string_list));
+ if (ctx->env.script_name)
+ ctx->cfg.script_name = xstrdup(ctx->env.script_name);
+ if (ctx->env.query_string)
+ ctx->qry.raw = xstrdup(ctx->env.query_string);
+ if (!ctx->env.cgit_config)
+ ctx->env.cgit_config = CGIT_CONFIG;
+}
+
+struct refmatch {
+ char *req_ref;
+ char *first_ref;
+ int match;
+};
+
+static 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;
+}
+
+static void free_refmatch_inner(struct refmatch *info)
+{
+ if (info->first_ref)
+ free(info->first_ref);
+}
+
+static char *find_default_branch(struct cgit_repo *repo)
+{
+ struct refmatch info;
+ char *ref;
+
+ info.req_ref = repo->defbranch;
+ info.first_ref = NULL;
+ info.match = 0;
+ for_each_branch_ref(find_current_ref, &info);
+ if (info.match)
+ ref = info.req_ref;
+ else
+ ref = info.first_ref;
+ if (ref)
+ ref = xstrdup(ref);
+ free_refmatch_inner(&info);
+
+ return ref;
+}
+
+static char *guess_defbranch(void)
+{
+ const char *ref;
+ unsigned char sha1[20];
+
+ ref = resolve_ref_unsafe("HEAD", sha1, 0, NULL);
+ if (!ref || prefixcmp(ref, "refs/heads/"))
+ return "master";
+ return xstrdup(ref + 11);
+}
+/* The caller must free filename and ref after calling this. */
+static inline void parse_readme(const char *readme, char **filename, char **ref, struct cgit_repo *repo)
+{
+ const char *colon;
+
+ *filename = NULL;
+ *ref = NULL;
+
+ if (!readme || !readme[0])
+ return;
+
+ /* Check if the readme is tracked in the git repo. */
+ colon = strchr(readme, ':');
+ if (colon && strlen(colon) > 1) {
+ /* If it starts with a colon, we want to use
+ * the default branch */
+ if (colon == readme && repo->defbranch)
+ *ref = xstrdup(repo->defbranch);
+ else
+ *ref = xstrndup(readme, colon - readme);
+ readme = colon + 1;
+ }
+
+ /* Prepend repo path to relative readme path unless tracked. */
+ if (!(*ref) && readme[0] != '/')
+ *filename = fmtalloc("%s/%s", repo->path, readme);
+ else
+ *filename = xstrdup(readme);
+}
+static void choose_readme(struct cgit_repo *repo)
+{
+ int found;
+ char *filename, *ref;
+ struct string_list_item *entry;
+
+ if (!repo->readme.nr)
+ return;
+
+ found = 0;
+ for_each_string_list_item(entry, &repo->readme) {
+ parse_readme(entry->string, &filename, &ref, repo);
+ if (!filename) {
+ free(filename);
+ free(ref);
+ continue;
+ }
+ /* If there's only one item, we skip the possibly expensive
+ * selection process. */
+ if (repo->readme.nr == 1) {
+ found = 1;
+ break;
+ }
+ if (ref) {
+ if (cgit_ref_path_exists(filename, ref, 1)) {
+ found = 1;
+ break;
+ }
+ }
+ else if (!access(filename, R_OK)) {
+ found = 1;
+ break;
+ }
+ free(filename);
+ free(ref);
+ }
+ repo->readme.strdup_strings = 1;
+ string_list_clear(&repo->readme, 0);
+ repo->readme.strdup_strings = 0;
+ if (found)
+ string_list_append(&repo->readme, filename)->util = ref;
+}
+
+static int prepare_repo_cmd(struct cgit_context *ctx)
+{
+ unsigned char sha1[20];
+ int nongit = 0;
+ int rc;
+
+ /* The path to the git repository. */
+ setenv("GIT_DIR", ctx->repo->path, 1);
+
+ /* Do not look in /etc/ for gitconfig and gitattributes. */
+ setenv("GIT_CONFIG_NOSYSTEM", "1", 1);
+ setenv("GIT_ATTR_NOSYSTEM", "1", 1);
+ unsetenv("HOME");
+ unsetenv("XDG_CONFIG_HOME");
+
+ /* Setup the git directory and initialize the notes system. Both of these
+ * load local configuration from the git repository, so we do them both while
+ * the HOME variables are unset. */
+ setup_git_directory_gently(&nongit);
+ init_display_notes(NULL);
+
+ if (nongit) {
+ const char *name = ctx->repo->name;
+ rc = errno;
+ ctx->page.title = fmtalloc("%s - %s", ctx->cfg.root_title,
+ "config error");
+ ctx->repo = NULL;
+ cgit_print_http_headers(ctx);
+ cgit_print_docstart(ctx);
+ cgit_print_pageheader(ctx);
+ cgit_print_error("Failed to open %s: %s", name,
+ rc ? strerror(rc) : "Not a valid git repository");
+ cgit_print_docend();
+ return 1;
+ }
+ ctx->page.title = fmtalloc("%s - %s", ctx->repo->name, ctx->repo->desc);
+
+ if (!ctx->repo->defbranch)
+ ctx->repo->defbranch = guess_defbranch();
+
+ if (!ctx->qry.head) {
+ ctx->qry.nohead = 1;
+ ctx->qry.head = find_default_branch(ctx->repo);
+ }
+
+ 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)) {
+ char *tmp = xstrdup(ctx->qry.head);
+ ctx->qry.head = ctx->repo->defbranch;
+ ctx->page.status = 404;
+ ctx->page.statusmsg = "Not found";
+ cgit_print_http_headers(ctx);
+ cgit_print_docstart(ctx);
+ cgit_print_pageheader(ctx);
+ cgit_print_error("Invalid branch: %s", tmp);
+ cgit_print_docend();
+ return 1;