int git_attr_file__load()

in src/attr_file.c [102:226]


int git_attr_file__load(
	git_attr_file **out,
	git_repository *repo,
	git_attr_session *attr_session,
	git_attr_file_entry *entry,
	git_attr_file_source source,
	git_attr_file_parser parser,
	bool allow_macros)
{
	int error = 0;
	git_tree *tree = NULL;
	git_tree_entry *tree_entry = NULL;
	git_blob *blob = NULL;
	git_buf content = GIT_BUF_INIT;
	const char *content_str;
	git_attr_file *file;
	struct stat st;
	bool nonexistent = false;
	int bom_offset;
	git_bom_t bom;
	git_oid id;
	git_object_size_t blobsize;

	*out = NULL;

	switch (source) {
	case GIT_ATTR_FILE__IN_MEMORY:
		/* in-memory attribute file doesn't need data */
		break;
	case GIT_ATTR_FILE__FROM_INDEX: {
		if ((error = attr_file_oid_from_index(&id, repo, entry->path)) < 0 ||
			(error = git_blob_lookup(&blob, repo, &id)) < 0)
			return error;

		/* Do not assume that data straight from the ODB is NULL-terminated;
		 * copy the contents of a file to a buffer to work on */
		blobsize = git_blob_rawsize(blob);

		GIT_ERROR_CHECK_BLOBSIZE(blobsize);
		git_buf_put(&content, git_blob_rawcontent(blob), (size_t)blobsize);
		break;
	}
	case GIT_ATTR_FILE__FROM_FILE: {
		int fd = -1;

		/* For open or read errors, pretend that we got ENOTFOUND. */
		/* TODO: issue warning when warning API is available */

		if (p_stat(entry->fullpath, &st) < 0 ||
			S_ISDIR(st.st_mode) ||
			(fd = git_futils_open_ro(entry->fullpath)) < 0 ||
			(error = git_futils_readbuffer_fd(&content, fd, (size_t)st.st_size)) < 0)
			nonexistent = true;

		if (fd >= 0)
			p_close(fd);

		break;
	}
	case GIT_ATTR_FILE__FROM_HEAD: {
		if ((error = git_repository_head_tree(&tree, repo)) < 0 ||
		    (error = git_tree_entry_bypath(&tree_entry, tree, entry->path)) < 0 ||
		    (error = git_blob_lookup(&blob, repo, git_tree_entry_id(tree_entry))) < 0)
			goto cleanup;

		/*
		 * Do not assume that data straight from the ODB is NULL-terminated;
		 * copy the contents of a file to a buffer to work on.
		 */
		blobsize = git_blob_rawsize(blob);

		GIT_ERROR_CHECK_BLOBSIZE(blobsize);
		if ((error = git_buf_put(&content,
			git_blob_rawcontent(blob), (size_t)blobsize)) < 0)
			goto cleanup;

		break;
	}
	default:
		git_error_set(GIT_ERROR_INVALID, "unknown file source %d", source);
		return -1;
	}

	if ((error = git_attr_file__new(&file, entry, source)) < 0)
		goto cleanup;

	/* advance over a UTF8 BOM */
	content_str = git_buf_cstr(&content);
	bom_offset = git_buf_text_detect_bom(&bom, &content);

	if (bom == GIT_BOM_UTF8)
		content_str += bom_offset;

	/* store the key of the attr_reader; don't bother with cache
	 * invalidation during the same attr reader session.
	 */
	if (attr_session)
		file->session_key = attr_session->key;

	if (parser && (error = parser(repo, file, content_str, allow_macros)) < 0) {
		git_attr_file__free(file);
		goto cleanup;
	}

	/* write cache breakers */
	if (nonexistent)
		file->nonexistent = 1;
	else if (source == GIT_ATTR_FILE__FROM_INDEX)
		git_oid_cpy(&file->cache_data.oid, git_blob_id(blob));
	else if (source == GIT_ATTR_FILE__FROM_HEAD)
		git_oid_cpy(&file->cache_data.oid, git_tree_id(tree));
	else if (source == GIT_ATTR_FILE__FROM_FILE)
		git_futils_filestamp_set_from_stat(&file->cache_data.stamp, &st);
	/* else always cacheable */

	*out = file;

cleanup:
	git_blob_free(blob);
	git_tree_entry_free(tree_entry);
	git_tree_free(tree);
	git_buf_dispose(&content);

	return error;
}