/* shared.c: global vars + some callback functions
 *
 * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
 *
 * Licensed under GNU General Public License v2
 *   (see COPYING for full license text)
 */

#include "cgit.h"

struct cgit_repolist cgit_repolist;
struct cgit_context ctx;

int chk_zero(int result, char *msg)
{
	if (result != 0)
		die_errno("%s", msg);
	return result;
}

int chk_positive(int result, char *msg)
{
	if (result <= 0)
		die_errno("%s", msg);
	return result;
}

int chk_non_negative(int result, char *msg)
{
	if (result < 0)
		die_errno("%s", msg);
	return result;
}

char *cgit_default_repo_desc = "[no description]";
struct cgit_repo *cgit_add_repo(const char *url)
{
	struct cgit_repo *ret;

	if (++cgit_repolist.count > cgit_repolist.length) {
		if (cgit_repolist.length == 0)
			cgit_repolist.length = 8;
		else
			cgit_repolist.length *= 2;
		cgit_repolist.repos = xrealloc(cgit_repolist.repos,
					       cgit_repolist.length *
					       sizeof(struct cgit_repo));
	}

	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 = cgit_default_repo_desc;
	ret->owner = NULL;
	ret->homepage = NULL;
	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->enable_html_serving = ctx.cfg.enable_html_serving;
	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 = 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->owner_filter = ctx.cfg.owner_filter;
	ret->clone_url = ctx.cfg.clone_url;
	ret->submodules.strdup_strings = 1;
	ret->hide = ret->ignore = 0;
	return ret;
}

struct cgit_repo *cgit_get_repoinfo(const char *url)
{
	int i;
	struct cgit_repo *repo;

	for (i = 0; i < cgit_repolist.count; i++) {
		repo = &cgit_repolist.repos[i];
		if (repo->ignore)
			continue;
		if (!strcmp(repo->url, url))
			return repo;
	}
	return NULL;
}

void cgit_free_commitinfo(struct commitinfo *info)
{
	free(info->author);
	free(info->author_email);
	free(info->committer);
	free(info->committer_email);
	free(info->subject);
	free(info->msg);
	free(info->msg_encoding);
	free(info);
}

char *trim_end(const char *str, char c)
{
	int len;

	if (str == NULL)
		return NULL;
	len = strlen(str);
	while (len > 0 && str[len - 1] == c)
		len--;
	if (len == 0)
		return NULL;
	return xstrndup(str, len);
}<style>pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */</style><div class="highlight"><pre><span></span><span class="cp">#ifndef UI_ATOM_H</span>
<span class="cp">#define UI_ATOM_H</span>

<span class="k">extern</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">cgit_print_atom</span><span class="p">(</span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="n">tip</span><span class="p">,</span><span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="n">path</span><span class="p">,</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">max_count</span><span class="p">);</span>

<span class="cp">#endif</span>
</pre></div>
</code></pre></td></tr></table>
</div> <!-- class=content -->
<div class='footer'>generated by <a href='https://git.zx2c4.com/cgit/about/'>cgit v1.2.3-54-g00ecf</a> (<a href='https://git-scm.com/'>git 2.39.0</a>) at 2025-03-19 17:46:17 +0000</div>
</div> <!-- id=cgit -->
</body>
</html>
okens, 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;
}

char *get_mimetype_for_filename(const char *filename)
{
	char *ext, *mimetype, *token, line[1024], *saveptr;
	FILE *file;
	struct string_list_item *mime;

	if (!filename)
		return NULL;

	ext = strrchr(filename, '.');
	if (!ext)
		return NULL;
	++ext;
	if (!ext[0])
		return NULL;
	mime = string_list_lookup(&ctx.cfg.mimetypes, ext);
	if (mime)
		return xstrdup(mime->util);

	if (!ctx.cfg.mimetype_file)
		return NULL;
	file = fopen(ctx.cfg.mimetype_file, "r");
	if (!file)
		return NULL;
	while (fgets(line, sizeof(line), file)) {
		if (!line[0] || line[0] == '#')
			continue;
		mimetype = strtok_r(line, " \t\r\n", &saveptr);
		while ((token = strtok_r(NULL, " \t\r\n", &saveptr))) {
			if (!strcasecmp(ext, token)) {
				fclose(file);
				return xstrdup(mimetype);
			}
		}
	}
	fclose(file);
	return NULL;
}