1#include "cache.h"2#include "commit.h"3#include "attr.h"4#include "archive.h"56static void format_subst(const struct commit *commit,7const char *src, size_t len,8struct strbuf *buf)9{10char *to_free = NULL;11struct strbuf fmt;1213if (src == buf->buf)14to_free = strbuf_detach(buf, NULL);15strbuf_init(&fmt, 0);16for (;;) {17const char *b, *c;1819b = memmem(src, len, "$Format:", 8);20if (!b)21break;22c = memchr(b + 8, '$', (src + len) - b - 8);23if (!c)24break;2526strbuf_reset(&fmt);27strbuf_add(&fmt, b + 8, c - b - 8);2829strbuf_add(buf, src, b - src);30format_commit_message(commit, fmt.buf, buf);31len -= c + 1 - src;32src = c + 1;33}34strbuf_add(buf, src, len);35strbuf_release(&fmt);36free(to_free);37}3839static void *sha1_file_to_archive(const char *path, const unsigned char *sha1,40unsigned int mode, enum object_type *type,41unsigned long *sizep, const struct commit *commit)42{43void *buffer;4445buffer = read_sha1_file(sha1, type, sizep);46if (buffer && S_ISREG(mode)) {47struct strbuf buf;48size_t size = 0;4950strbuf_init(&buf, 0);51strbuf_attach(&buf, buffer, *sizep, *sizep + 1);52convert_to_working_tree(path, buf.buf, buf.len, &buf);53if (commit)54format_subst(commit, buf.buf, buf.len, &buf);55buffer = strbuf_detach(&buf, &size);56*sizep = size;57}5859return buffer;60}6162static void setup_archive_check(struct git_attr_check *check)63{64static struct git_attr *attr_export_ignore;65static struct git_attr *attr_export_subst;6667if (!attr_export_ignore) {68attr_export_ignore = git_attr("export-ignore", 13);69attr_export_subst = git_attr("export-subst", 12);70}71check[0].attr = attr_export_ignore;72check[1].attr = attr_export_subst;73}7475struct archiver_context {76struct archiver_args *args;77write_archive_entry_fn_t write_entry;78};7980static int write_archive_entry(const unsigned char *sha1, const char *base,81int baselen, const char *filename, unsigned mode, int stage,82void *context)83{84static struct strbuf path = STRBUF_INIT;85struct archiver_context *c = context;86struct archiver_args *args = c->args;87write_archive_entry_fn_t write_entry = c->write_entry;88struct git_attr_check check[2];89const char *path_without_prefix;90int convert = 0;91int err;92enum object_type type;93unsigned long size;94void *buffer;9596strbuf_reset(&path);97strbuf_grow(&path, PATH_MAX);98strbuf_add(&path, base, baselen);99strbuf_addstr(&path, filename);100path_without_prefix = path.buf + args->baselen;101102setup_archive_check(check);103if (!git_checkattr(path_without_prefix, ARRAY_SIZE(check), check)) {104if (ATTR_TRUE(check[0].value))105return 0;106convert = ATTR_TRUE(check[1].value);107}108109if (S_ISDIR(mode) || S_ISGITLINK(mode)) {110strbuf_addch(&path, '/');111if (args->verbose)112fprintf(stderr, "%.*s\n", (int)path.len, path.buf);113err = write_entry(args, sha1, path.buf, path.len, mode, NULL, 0);114if (err)115return err;116return READ_TREE_RECURSIVE;117}118119buffer = sha1_file_to_archive(path_without_prefix, sha1, mode,120&type, &size, convert ? args->commit : NULL);121if (!buffer)122return error("cannot read %s", sha1_to_hex(sha1));123if (args->verbose)124fprintf(stderr, "%.*s\n", (int)path.len, path.buf);125err = write_entry(args, sha1, path.buf, path.len, mode, buffer, size);126free(buffer);127return err;128}129130int write_archive_entries(struct archiver_args *args,131write_archive_entry_fn_t write_entry)132{133struct archiver_context context;134int err;135136if (args->baselen > 0 && args->base[args->baselen - 1] == '/') {137size_t len = args->baselen;138139while (len > 1 && args->base[len - 2] == '/')140len--;141if (args->verbose)142fprintf(stderr, "%.*s\n", (int)len, args->base);143err = write_entry(args, args->tree->object.sha1, args->base,144len, 040777, NULL, 0);145if (err)146return err;147}148149context.args = args;150context.write_entry = write_entry;151152err = read_tree_recursive(args->tree, args->base, args->baselen, 0,153args->pathspec, write_archive_entry, &context);154if (err == READ_TREE_RECURSIVE)155err = 0;156return err;157}