+ 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;