Commit e3d55b2a by Vicent Martí

Merge pull request #575 from libgit2/filters

Filters, yo
parents 17b3d9b9 ce49c7a8
......@@ -19,12 +19,12 @@
*/
GIT_BEGIN_DECL
#define GIT_ATTR_TRUE git_attr__true
#define GIT_ATTR_FALSE git_attr__false
#define GIT_ATTR_UNSPECIFIED NULL
#define GIT_ATTR_TRUE(attr) ((attr) == git_attr__true)
#define GIT_ATTR_FALSE(attr) ((attr) == git_attr__false)
#define GIT_ATTR_UNSPECIFIED(attr) ((attr) == NULL)
GIT_EXTERN(const char *)git_attr__true;
GIT_EXTERN(const char *)git_attr__false;
GIT_EXTERN(const char *) git_attr__true;
GIT_EXTERN(const char *) git_attr__false;
/**
......
......@@ -37,6 +37,19 @@ struct git_config_file {
void (*free)(struct git_config_file *);
};
typedef enum {
GIT_CVAR_FALSE = 0,
GIT_CVAR_TRUE = 1,
GIT_CVAR_INT32,
GIT_CVAR_STRING
} git_cvar_t;
typedef struct {
git_cvar_t cvar_type;
const char *str_match;
int map_value;
} git_cvar_map;
/**
* Locate the path to the global configuration file
*
......@@ -301,6 +314,43 @@ GIT_EXTERN(int) git_config_foreach(
int (*callback)(const char *var_name, const char *value, void *payload),
void *payload);
/**
* Query the value of a config variable and return it mapped to
* an integer constant.
*
* This is a helper method to easily map different possible values
* to a variable to integer constants that easily identify them.
*
* A mapping array looks as follows:
*
* git_cvar_map autocrlf_mapping[3] = {
* {GIT_CVAR_FALSE, NULL, GIT_AUTO_CRLF_FALSE},
* {GIT_CVAR_TRUE, NULL, GIT_AUTO_CRLF_TRUE},
* {GIT_CVAR_STRING, "input", GIT_AUTO_CRLF_INPUT},
* {GIT_CVAR_STRING, "default", GIT_AUTO_CRLF_DEFAULT}};
*
* On any "false" value for the variable (e.g. "false", "FALSE", "no"), the
* mapping will store `GIT_AUTO_CRLF_FALSE` in the `out` parameter.
*
* The same thing applies for any "true" value such as "true", "yes" or "1", storing
* the `GIT_AUTO_CRLF_TRUE` variable.
*
* Otherwise, if the value matches the string "input" (with case insensitive comparison),
* the given constant will be stored in `out`, and likewise for "default".
*
* If not a single match can be made to store in `out`, an error code will be
* returned.
*
* @param cfg config file to get the variables from
* @param name name of the config variable to lookup
* @param maps array of `git_cvar_map` objects specifying the possible mappings
* @param map_n number of mapping objects in `maps`
* @param out place to store the result of the mapping
* @return GIT_SUCCESS on success, error code otherwise
*/
GIT_EXTERN(int) git_config_get_mapped(git_config *cfg, const char *name, git_cvar_map *maps, size_t map_n, int *out);
/** @} */
GIT_END_DECL
#endif
......@@ -111,7 +111,7 @@ int git_attr_file__from_file(
git_repository *repo, const char *path, git_attr_file *file)
{
int error = GIT_SUCCESS;
git_fbuffer fbuf = GIT_FBUFFER_INIT;
git_buf fbuf = GIT_BUF_INIT;
assert(path && file);
......@@ -120,9 +120,9 @@ int git_attr_file__from_file(
if (error == GIT_SUCCESS &&
(error = git_futils_readbuffer(&fbuf, path)) == GIT_SUCCESS)
error = git_attr_file__from_buffer(repo, fbuf.data, file);
error = git_attr_file__from_buffer(repo, fbuf.ptr, file);
git_futils_freebuffer(&fbuf);
git_buf_free(&fbuf);
if (error != GIT_SUCCESS)
git__rethrow(error, "Could not open attribute file '%s'", path);
......@@ -458,12 +458,12 @@ int git_attr_assignment__parse(
}
assign->name_hash = 5381;
assign->value = GIT_ATTR_TRUE;
assign->value = git_attr__true;
assign->is_allocated = 0;
/* look for magic name prefixes */
if (*scan == '-') {
assign->value = GIT_ATTR_FALSE;
assign->value = git_attr__false;
scan++;
} else if (*scan == '!') {
assign->value = NULL; /* explicit unspecified state */
......@@ -510,7 +510,7 @@ int git_attr_assignment__parse(
}
/* expand macros (if given a repo with a macro cache) */
if (repo != NULL && assign->value == GIT_ATTR_TRUE) {
if (repo != NULL && assign->value == git_attr__true) {
git_attr_rule *macro =
git_hashtable_lookup(repo->attrcache.macros, assign->name);
......
......@@ -11,6 +11,7 @@
#include "common.h"
#include "blob.h"
#include "filter.h"
const void *git_blob_rawcontent(git_blob *blob)
{
......@@ -24,6 +25,12 @@ size_t git_blob_rawsize(git_blob *blob)
return blob->odb_object->raw.len;
}
int git_blob__getbuf(git_buf *buffer, git_blob *blob)
{
return git_buf_set(
buffer, blob->odb_object->raw.data, blob->odb_object->raw.len);
}
void git_blob__free(git_blob *blob)
{
git_odb_object_free(blob->odb_object);
......@@ -65,15 +72,100 @@ int git_blob_create_frombuffer(git_oid *oid, git_repository *repo, const void *b
return GIT_SUCCESS;
}
static int write_file_stream(git_oid *oid, git_odb *odb, const char *path, git_off_t file_size)
{
int fd, error;
char buffer[4096];
git_odb_stream *stream = NULL;
if ((error = git_odb_open_wstream(&stream, odb, file_size, GIT_OBJ_BLOB)) < GIT_SUCCESS)
return error;
if ((fd = p_open(path, O_RDONLY)) < 0) {
error = git__throw(GIT_ENOTFOUND, "Failed to create blob. Could not open '%s'", path);
goto cleanup;
}
while (file_size > 0) {
ssize_t read_len = p_read(fd, buffer, sizeof(buffer));
if (read_len < 0) {
error = git__throw(GIT_EOSERR, "Failed to create blob. Can't read full file");
p_close(fd);
goto cleanup;
}
stream->write(stream, buffer, read_len);
file_size -= read_len;
}
p_close(fd);
error = stream->finalize_write(oid, stream);
cleanup:
stream->free(stream);
return error;
}
static int write_file_filtered(
git_oid *oid,
git_odb *odb,
const char *full_path,
git_vector *filters)
{
int error;
git_buf source = GIT_BUF_INIT;
git_buf dest = GIT_BUF_INIT;
error = git_futils_readbuffer(&source, full_path);
if (error < GIT_SUCCESS)
return error;
error = git_filters_apply(&dest, &source, filters);
/* Free the source as soon as possible. This can be big in memory,
* and we don't want to ODB write to choke */
git_buf_free(&source);
if (error == GIT_SUCCESS) {
/* Write the file to disk if it was properly filtered */
error = git_odb_write(oid, odb, dest.ptr, dest.size, GIT_OBJ_BLOB);
}
git_buf_free(&dest);
return GIT_SUCCESS;
}
static int write_symlink(git_oid *oid, git_odb *odb, const char *path, size_t link_size)
{
char *link_data;
ssize_t read_len;
int error;
link_data = git__malloc(link_size);
if (!link_data)
return GIT_ENOMEM;
read_len = p_readlink(path, link_data, link_size);
if (read_len != (ssize_t)link_size) {
free(link_data);
return git__throw(GIT_EOSERR, "Failed to create blob. Can't read symlink");
}
error = git_odb_write(oid, odb, (void *)link_data, link_size, GIT_OBJ_BLOB);
free(link_data);
return error;
}
int git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *path)
{
int error = GIT_SUCCESS;
git_buf full_path = GIT_BUF_INIT;
git_off_t size;
git_odb_stream *stream = NULL;
struct stat st;
const char *workdir;
git_odb *odb;
git_odb *odb = NULL;
workdir = git_repository_workdir(repo);
if (workdir == NULL)
......@@ -95,63 +187,45 @@ int git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *pat
if (error < GIT_SUCCESS)
goto cleanup;
if ((error = git_odb_open_wstream(&stream, odb, (size_t)size, GIT_OBJ_BLOB)) < GIT_SUCCESS)
goto cleanup;
if (S_ISLNK(st.st_mode)) {
char *link_data;
ssize_t read_len;
link_data = git__malloc((size_t)size);
if (!link_data) {
error = GIT_ENOMEM;
goto cleanup;
}
read_len = p_readlink(full_path.ptr, link_data, (size_t)size);
if (read_len != (ssize_t)size) {
error = git__throw(GIT_EOSERR, "Failed to create blob. Can't read symlink");
free(link_data);
goto cleanup;
}
stream->write(stream, link_data, (size_t)size);
free(link_data);
error = write_symlink(oid, odb, full_path.ptr, (size_t)size);
} else {
int fd;
char buffer[2048];
if ((fd = p_open(full_path.ptr, O_RDONLY)) < 0) {
error = git__throw(GIT_ENOTFOUND, "Failed to create blob. Could not open '%s'", full_path.ptr);
goto cleanup;
}
while (size > 0) {
ssize_t read_len = p_read(fd, buffer, sizeof(buffer));
git_vector write_filters = GIT_VECTOR_INIT;
int filter_count;
if (read_len < 0) {
error = git__throw(GIT_EOSERR, "Failed to create blob. Can't read full file");
p_close(fd);
goto cleanup;
}
/* Load the filters for writing this file to the ODB */
filter_count = git_filters_load(&write_filters, repo, path, GIT_FILTER_TO_ODB);
stream->write(stream, buffer, read_len);
size -= read_len;
if (filter_count < 0) {
/* Negative value means there was a critical error */
error = filter_count;
goto cleanup;
} else if (filter_count == 0) {
/* No filters need to be applied to the document: we can stream
* directly from disk */
error = write_file_stream(oid, odb, full_path.ptr, size);
} else {
/* We need to apply one or more filters */
error = write_file_filtered(oid, odb, full_path.ptr, &write_filters);
}
p_close(fd);
git_filters_free(&write_filters);
/*
* TODO: eventually support streaming filtered files, for files which are bigger
* than a given threshold. This is not a priority because applying a filter in
* streaming mode changes the final size of the blob, and without knowing its
* final size, the blob cannot be written in stream mode to the ODB.
*
* The plan is to do streaming writes to a tempfile on disk and then opening
* streaming that file to the ODB, using `write_file_stream`.
*
* CAREFULLY DESIGNED APIS YO
*/
}
error = stream->finalize_write(oid, stream);
cleanup:
if (stream)
stream->free(stream);
git_buf_free(&full_path);
return error;
}
......@@ -19,5 +19,6 @@ struct git_blob {
void git_blob__free(git_blob *blob);
int git_blob__parse(git_blob *blob, git_odb_object *obj);
int git_blob__getbuf(git_buf *buffer, git_blob *blob);
#endif
......@@ -7,14 +7,17 @@
#include "buffer.h"
#include "posix.h"
#include <stdarg.h>
#include <ctype.h>
/* Used as default value for git_buf->ptr so that people can always
* assume ptr is non-NULL and zero terminated even for new git_bufs.
*/
char git_buf_initbuf[1];
static char git_buf__oom;
#define ENSURE_SIZE(b, d) \
if ((ssize_t)(d) > buf->asize && git_buf_grow(b, (d)) < GIT_SUCCESS)\
if ((d) > buf->asize && git_buf_grow(b, (d)) < GIT_SUCCESS)\
return GIT_ENOMEM;
......@@ -31,8 +34,10 @@ void git_buf_init(git_buf *buf, size_t initial_size)
int git_buf_grow(git_buf *buf, size_t target_size)
{
int error = git_buf_try_grow(buf, target_size);
if (error != GIT_SUCCESS)
buf->asize = -1;
if (error != GIT_SUCCESS) {
buf->ptr = &git_buf__oom;
}
return error;
}
......@@ -41,17 +46,17 @@ int git_buf_try_grow(git_buf *buf, size_t target_size)
char *new_ptr;
size_t new_size;
if (buf->asize < 0)
if (buf->ptr == &git_buf__oom)
return GIT_ENOMEM;
if (target_size <= (size_t)buf->asize)
if (target_size <= buf->asize)
return GIT_SUCCESS;
if (buf->asize == 0) {
new_size = target_size;
new_ptr = NULL;
} else {
new_size = (size_t)buf->asize;
new_size = buf->asize;
new_ptr = buf->ptr;
}
......@@ -64,7 +69,6 @@ int git_buf_try_grow(git_buf *buf, size_t target_size)
new_size = (new_size + 7) & ~7;
new_ptr = git__realloc(new_ptr, new_size);
/* if realloc fails, return without modifying the git_buf */
if (!new_ptr)
return GIT_ENOMEM;
......@@ -83,7 +87,7 @@ void git_buf_free(git_buf *buf)
{
if (!buf) return;
if (buf->ptr != git_buf_initbuf)
if (buf->ptr != git_buf_initbuf && buf->ptr != &git_buf__oom)
git__free(buf->ptr);
git_buf_init(buf, 0);
......@@ -98,12 +102,12 @@ void git_buf_clear(git_buf *buf)
int git_buf_oom(const git_buf *buf)
{
return (buf->asize < 0);
return (buf->ptr == &git_buf__oom);
}
int git_buf_lasterror(const git_buf *buf)
{
return (buf->asize < 0) ? GIT_ENOMEM : GIT_SUCCESS;
return (buf->ptr == &git_buf__oom) ? GIT_ENOMEM : GIT_SUCCESS;
}
int git_buf_set(git_buf *buf, const char *data, size_t len)
......@@ -162,11 +166,12 @@ int git_buf_printf(git_buf *buf, const char *format, ...)
va_end(arglist);
if (len < 0) {
buf->asize = -1;
free(buf->ptr);
buf->ptr = &git_buf__oom;
return GIT_ENOMEM;
}
if (len + 1 <= buf->asize - buf->size) {
if ((size_t)len + 1 <= buf->asize - buf->size) {
buf->size += len;
break;
}
......@@ -205,9 +210,9 @@ void git_buf_consume(git_buf *buf, const char *end)
}
}
void git_buf_truncate(git_buf *buf, ssize_t len)
void git_buf_truncate(git_buf *buf, size_t len)
{
if (len >= 0 && len < buf->size) {
if (len < buf->size) {
buf->size = len;
buf->ptr[buf->size] = '\0';
}
......@@ -230,7 +235,7 @@ char *git_buf_detach(git_buf *buf)
{
char *data = buf->ptr;
if (buf->asize <= 0)
if (buf->asize == 0 || buf->ptr == &git_buf__oom)
return NULL;
git_buf_init(buf, 0);
......@@ -238,7 +243,7 @@ char *git_buf_detach(git_buf *buf)
return data;
}
void git_buf_attach(git_buf *buf, char *ptr, ssize_t asize)
void git_buf_attach(git_buf *buf, char *ptr, size_t asize)
{
git_buf_free(buf);
......@@ -372,3 +377,22 @@ int git_buf_join(
return error;
}
void git_buf_rtrim(git_buf *buf)
{
while (buf->size > 0) {
if (!isspace(buf->ptr[buf->size - 1]))
break;
buf->size--;
}
buf->ptr[buf->size] = '\0';
}
int git_buf_cmp(const git_buf *a, const git_buf *b)
{
int result = memcmp(a->ptr, b->ptr, min(a->size, b->size));
return (result != 0) ? result :
(a->size < b->size) ? -1 : (a->size > b->size) ? 1 : 0;
}
......@@ -11,7 +11,7 @@
typedef struct {
char *ptr;
ssize_t asize, size;
size_t asize, size;
} git_buf;
extern char git_buf_initbuf[];
......@@ -47,7 +47,7 @@ int git_buf_try_grow(git_buf *buf, size_t target_size);
void git_buf_free(git_buf *buf);
void git_buf_swap(git_buf *buf_a, git_buf *buf_b);
char *git_buf_detach(git_buf *buf);
void git_buf_attach(git_buf *buf, char *ptr, ssize_t asize);
void git_buf_attach(git_buf *buf, char *ptr, size_t asize);
/**
* Test if there have been any reallocation failures with this git_buf.
......@@ -83,7 +83,7 @@ int git_buf_puts(git_buf *buf, const char *string);
int git_buf_printf(git_buf *buf, const char *format, ...) GIT_FORMAT_PRINTF(2, 3);
void git_buf_clear(git_buf *buf);
void git_buf_consume(git_buf *buf, const char *end);
void git_buf_truncate(git_buf *buf, ssize_t len);
void git_buf_truncate(git_buf *buf, size_t len);
void git_buf_rtruncate_at_char(git_buf *path, char separator);
int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...);
......@@ -115,4 +115,9 @@ GIT_INLINE(int) git_buf_rfind_next(git_buf *buf, char ch)
return idx;
}
/* Remove whitespace from the end of the buffer */
void git_buf_rtrim(git_buf *buf);
int git_buf_cmp(const git_buf *a, const git_buf *b);
#endif
......@@ -209,23 +209,37 @@ int git_config_set_string(git_config *cfg, const char *name, const char *value)
return file->set(file, name, value);
}
/***********
* Getters
***********/
static int parse_bool(int *out, const char *value)
{
/* A missing value means true */
if (value == NULL) {
*out = 1;
return GIT_SUCCESS;
}
int git_config_get_int64(git_config *cfg, const char *name, int64_t *out)
if (!strcasecmp(value, "true") ||
!strcasecmp(value, "yes") ||
!strcasecmp(value, "on")) {
*out = 1;
return GIT_SUCCESS;
}
if (!strcasecmp(value, "false") ||
!strcasecmp(value, "no") ||
!strcasecmp(value, "off")) {
*out = 0;
return GIT_SUCCESS;
}
return GIT_EINVALIDTYPE;
}
static int parse_int64(int64_t *out, const char *value)
{
const char *value, *num_end;
int ret;
const char *num_end;
int64_t num;
ret = git_config_get_string(cfg, name, &value);
if (ret < GIT_SUCCESS)
return git__rethrow(ret, "Failed to retrieve value for '%s'", name);
ret = git__strtol64(&num, value, &num_end, 0);
if (ret < GIT_SUCCESS)
return git__rethrow(ret, "Failed to convert value for '%s'", name);
if (git__strtol64(&num, value, &num_end, 0) < 0)
return GIT_EINVALIDTYPE;
switch (*num_end) {
case 'g':
......@@ -245,38 +259,112 @@ int git_config_get_int64(git_config *cfg, const char *name, int64_t *out)
/* check that that there are no more characters after the
* given modifier suffix */
if (num_end[1] != '\0')
return git__throw(GIT_EINVALIDTYPE,
"Failed to get value for '%s'. Invalid type suffix", name);
return GIT_EINVALIDTYPE;
/* fallthrough */
case '\0':
*out = num;
return GIT_SUCCESS;
return 0;
default:
return git__throw(GIT_EINVALIDTYPE,
"Failed to get value for '%s'. Value is of invalid type", name);
return GIT_EINVALIDTYPE;
}
}
int git_config_get_int32(git_config *cfg, const char *name, int32_t *out)
static int parse_int32(int32_t *out, const char *value)
{
int64_t tmp_long;
int32_t tmp_int;
int64_t tmp;
int32_t truncate;
if (parse_int64(&tmp, value) < 0)
return GIT_EINVALIDTYPE;
truncate = tmp & 0xFFFFFFFF;
if (truncate != tmp)
return GIT_EOVERFLOW;
*out = truncate;
return 0;
}
/***********
* Getters
***********/
int git_config_get_mapped(git_config *cfg, const char *name, git_cvar_map *maps, size_t map_n, int *out)
{
size_t i;
const char *value;
int error;
error = git_config_get_string(cfg, name, &value);
if (error < GIT_SUCCESS)
return error;
for (i = 0; i < map_n; ++i) {
git_cvar_map *m = maps + i;
switch (m->cvar_type) {
case GIT_CVAR_FALSE:
case GIT_CVAR_TRUE: {
int bool_val;
if (parse_bool(&bool_val, value) == 0 &&
bool_val == (int)m->cvar_type) {
*out = m->map_value;
return 0;
}
break;
}
case GIT_CVAR_INT32:
if (parse_int32(out, value) == 0)
return 0;
break;
case GIT_CVAR_STRING:
if (strcasecmp(value, m->str_match) == 0) {
*out = m->map_value;
return 0;
}
}
}
return git__throw(GIT_ENOTFOUND,
"Failed to map the '%s' config variable with a valid value", name);
}
int git_config_get_int64(git_config *cfg, const char *name, int64_t *out)
{
const char *value;
int ret;
ret = git_config_get_int64(cfg, name, &tmp_long);
ret = git_config_get_string(cfg, name, &value);
if (ret < GIT_SUCCESS)
return git__rethrow(ret, "Failed to convert value for '%s'", name);
tmp_int = tmp_long & 0xFFFFFFFF;
if (tmp_int != tmp_long)
return git__throw(GIT_EOVERFLOW, "Value for '%s' is too large", name);
return git__rethrow(ret, "Failed to retrieve value for '%s'", name);
*out = tmp_int;
if (parse_int64(out, value) < 0)
return git__throw(GIT_EINVALIDTYPE, "Failed to parse '%s' as an integer", value);
return ret;
return GIT_SUCCESS;
}
int git_config_get_int32(git_config *cfg, const char *name, int32_t *out)
{
const char *value;
int error;
error = git_config_get_string(cfg, name, &value);
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to get value for %s", name);
error = parse_int32(out, value);
if (error < GIT_SUCCESS)
return git__throw(GIT_EINVALIDTYPE, "Failed to parse '%s' as a 32-bit integer", value);
return GIT_SUCCESS;
}
int git_config_get_bool(git_config *cfg, const char *name, int *out)
......@@ -288,33 +376,15 @@ int git_config_get_bool(git_config *cfg, const char *name, int *out)
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to get value for %s", name);
/* A missing value means true */
if (value == NULL) {
*out = 1;
if (parse_bool(out, value) == 0)
return GIT_SUCCESS;
}
if (!strcasecmp(value, "true") ||
!strcasecmp(value, "yes") ||
!strcasecmp(value, "on")) {
*out = 1;
return GIT_SUCCESS;
}
if (!strcasecmp(value, "false") ||
!strcasecmp(value, "no") ||
!strcasecmp(value, "off")) {
*out = 0;
if (parse_int32(out, value) == 0) {
*out = !!(*out);
return GIT_SUCCESS;
}
/* Try to parse it as an integer */
error = git_config_get_int32(cfg, name, out);
if (error == GIT_SUCCESS)
*out = !!(*out);
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to get value for %s", name);
return error;
return git__throw(GIT_EINVALIDTYPE, "Failed to parse '%s' as a boolean value", value);
}
int git_config_get_string(git_config *cfg, const char *name, const char **out)
......
/*
* Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#include "common.h"
#include "fileops.h"
#include "hashtable.h"
#include "config.h"
#include "git2/config.h"
#include "vector.h"
#include "filter.h"
#include "repository.h"
struct map_data {
const char *cvar_name;
git_cvar_map *maps;
size_t map_count;
int default_value;
};
/*
* core.eol
* Sets the line ending type to use in the working directory for
* files that have the text property set. Alternatives are lf, crlf
* and native, which uses the platform’s native line ending. The default
* value is native. See gitattributes(5) for more information on
* end-of-line conversion.
*/
static git_cvar_map _cvar_map_eol[] = {
{GIT_CVAR_FALSE, NULL, GIT_EOL_UNSET},
{GIT_CVAR_STRING, "lf", GIT_EOL_LF},
{GIT_CVAR_STRING, "crlf", GIT_EOL_CRLF},
{GIT_CVAR_STRING, "native", GIT_EOL_NATIVE}
};
/*
* core.autocrlf
* Setting this variable to "true" is almost the same as setting
* the text attribute to "auto" on all files except that text files are
* not guaranteed to be normalized: files that contain CRLF in the
* repository will not be touched. Use this setting if you want to have
* CRLF line endings in your working directory even though the repository
* does not have normalized line endings. This variable can be set to input,
* in which case no output conversion is performed.
*/
static git_cvar_map _cvar_map_autocrlf[] = {
{GIT_CVAR_FALSE, NULL, GIT_AUTO_CRLF_FALSE},
{GIT_CVAR_TRUE, NULL, GIT_AUTO_CRLF_TRUE},
{GIT_CVAR_STRING, "input", GIT_AUTO_CRLF_INPUT}
};
static struct map_data _cvar_maps[] = {
{"core.autocrlf", _cvar_map_autocrlf, ARRAY_SIZE(_cvar_map_autocrlf), GIT_AUTO_CRLF_DEFAULT},
{"core.eol", _cvar_map_eol, ARRAY_SIZE(_cvar_map_eol), GIT_EOL_DEFAULT}
};
int git_repository__cvar(int *out, git_repository *repo, git_cvar_cached cvar)
{
*out = repo->cvar_cache[(int)cvar];
if (*out == GIT_CVAR_NOT_CACHED) {
struct map_data *data = &_cvar_maps[(int)cvar];
git_config *config;
int error;
error = git_repository_config__weakptr(&config, repo);
if (error < GIT_SUCCESS)
return error;
error = git_config_get_mapped(
config, data->cvar_name, data->maps, data->map_count, out);
if (error == GIT_ENOTFOUND)
*out = data->default_value;
else if (error < GIT_SUCCESS)
return error;
repo->cvar_cache[(int)cvar] = *out;
}
return GIT_SUCCESS;
}
void git_repository__cvar_cache_clear(git_repository *repo)
{
int i;
for (i = 0; i < GIT_CVAR_CACHE_MAX; ++i)
repo->cvar_cache[i] = GIT_CVAR_NOT_CACHED;
}
......@@ -73,7 +73,7 @@ typedef struct {
git_hashtable *values;
struct {
git_fbuffer buffer;
git_buf buffer;
char *read_ptr;
int line_number;
int eof;
......@@ -151,6 +151,7 @@ static int config_open(git_config_file *cfg)
if (b->values == NULL)
return GIT_ENOMEM;
git_buf_init(&b->reader.buffer, 0);
error = git_futils_readbuffer(&b->reader.buffer, b->file_path);
/* It's fine if the file doesn't exist */
......@@ -164,14 +165,14 @@ static int config_open(git_config_file *cfg)
if (error < GIT_SUCCESS)
goto cleanup;
git_futils_freebuffer(&b->reader.buffer);
git_buf_free(&b->reader.buffer);
return GIT_SUCCESS;
cleanup:
free_vars(b->values);
b->values = NULL;
git_futils_freebuffer(&b->reader.buffer);
git_buf_free(&b->reader.buffer);
return git__rethrow(error, "Failed to open config");
}
......@@ -765,7 +766,7 @@ static int skip_bom(diskfile_backend *cfg)
{
static const char utf8_bom[] = "\xef\xbb\xbf";
if (cfg->reader.buffer.len < sizeof(utf8_bom))
if (cfg->reader.buffer.size < sizeof(utf8_bom))
return GIT_SUCCESS;
if (memcmp(cfg->reader.read_ptr, utf8_bom, sizeof(utf8_bom)) == 0)
......@@ -847,7 +848,7 @@ static int config_parse(diskfile_backend *cfg_file)
git_buf buf = GIT_BUF_INIT;
/* Initialize the reading position */
cfg_file->reader.read_ptr = cfg_file->reader.buffer.data;
cfg_file->reader.read_ptr = cfg_file->reader.buffer.ptr;
cfg_file->reader.eof = 0;
/* If the file is empty, there's nothing for us to do */
......@@ -976,10 +977,9 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p
cfg->reader.read_ptr = NULL;
cfg->reader.eof = 1;
data_start = NULL;
cfg->reader.buffer.len = 0;
cfg->reader.buffer.data = NULL;
git_buf_clear(&cfg->reader.buffer);
} else {
cfg->reader.read_ptr = cfg->reader.buffer.data;
cfg->reader.read_ptr = cfg->reader.buffer.ptr;
cfg->reader.eof = 0;
data_start = cfg->reader.read_ptr;
}
......@@ -1093,7 +1093,7 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p
/* And then the write out rest of the file */
error = git_filebuf_write(&file, post_start,
cfg->reader.buffer.len - (post_start - data_start));
cfg->reader.buffer.size - (post_start - data_start));
if (error < GIT_SUCCESS) {
git__rethrow(error, "Failed to write the rest of the file");
......@@ -1128,7 +1128,7 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p
goto cleanup;
}
error = git_filebuf_write(&file, cfg->reader.buffer.data, cfg->reader.buffer.len);
error = git_filebuf_write(&file, cfg->reader.buffer.ptr, cfg->reader.buffer.size);
if (error < GIT_SUCCESS) {
git__rethrow(error, "Failed to write original config content");
goto cleanup;
......@@ -1155,7 +1155,7 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p
else
error = git_filebuf_commit(&file, GIT_CONFIG_FILE_MODE);
git_futils_freebuffer(&cfg->reader.buffer);
git_buf_free(&cfg->reader.buffer);
return error;
}
......
/*
* Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#include "common.h"
#include "fileops.h"
#include "hash.h"
#include "filter.h"
#include "repository.h"
#include "git2/attr.h"
struct crlf_attrs {
int crlf_action;
int eol;
};
struct crlf_filter {
git_filter f;
struct crlf_attrs attrs;
};
static int check_crlf(const char *value)
{
if (GIT_ATTR_TRUE(value))
return GIT_CRLF_TEXT;
if (GIT_ATTR_FALSE(value))
return GIT_CRLF_BINARY;
if (GIT_ATTR_UNSPECIFIED(value))
return GIT_CRLF_GUESS;
if (strcmp(value, "input") == 0)
return GIT_CRLF_INPUT;
if (strcmp(value, "auto") == 0)
return GIT_CRLF_AUTO;
return GIT_CRLF_GUESS;
}
static int check_eol(const char *value)
{
if (GIT_ATTR_UNSPECIFIED(value))
return GIT_EOL_UNSET;
if (strcmp(value, "lf") == 0)
return GIT_EOL_LF;
if (strcmp(value, "crlf") == 0)
return GIT_EOL_CRLF;
return GIT_EOL_UNSET;
}
static int crlf_input_action(struct crlf_attrs *ca)
{
if (ca->crlf_action == GIT_CRLF_BINARY)
return GIT_CRLF_BINARY;
if (ca->eol == GIT_EOL_LF)
return GIT_CRLF_INPUT;
if (ca->eol == GIT_EOL_CRLF)
return GIT_CRLF_CRLF;
return ca->crlf_action;
}
static int crlf_load_attributes(struct crlf_attrs *ca, git_repository *repo, const char *path)
{
#define NUM_CONV_ATTRS 3
static const char *attr_names[NUM_CONV_ATTRS] = {
"crlf", "eol", "text",
};
const char *attr_vals[NUM_CONV_ATTRS];
int error;
error = git_attr_get_many(repo, path, NUM_CONV_ATTRS, attr_names, attr_vals);
if (error == GIT_ENOTFOUND) {
ca->crlf_action = GIT_CRLF_GUESS;
ca->eol = GIT_EOL_UNSET;
return 0;
}
if (error == GIT_SUCCESS) {
ca->crlf_action = check_crlf(attr_vals[2]); /* text */
if (ca->crlf_action == GIT_CRLF_GUESS)
ca->crlf_action = check_crlf(attr_vals[0]); /* clrf */
ca->eol = check_eol(attr_vals[1]); /* eol */
return 0;
}
return error;
}
static int drop_crlf(git_buf *dest, const git_buf *source)
{
const char *scan = source->ptr, *next;
const char *scan_end = source->ptr + source->size;
/* Main scan loop. Find the next carriage return and copy the
* whole chunk up to that point to the destination buffer.
*/
while ((next = memchr(scan, '\r', scan_end - scan)) != NULL) {
/* copy input up to \r */
if (next > scan)
git_buf_put(dest, scan, next - scan);
/* Do not drop \r unless it is followed by \n */
if (*(next + 1) != '\n')
git_buf_putc(dest, '\r');
scan = next + 1;
}
/* If there was no \r, then tell the library to skip this filter */
if (scan == source->ptr)
return -1;
/* Copy remaining input into dest */
git_buf_put(dest, scan, scan_end - scan);
return git_buf_lasterror(dest);
}
static int crlf_apply_to_odb(git_filter *self, git_buf *dest, const git_buf *source)
{
struct crlf_filter *filter = (struct crlf_filter *)self;
assert(self && dest && source);
/* Empty file? Nothing to do */
if (source->size == 0)
return 0;
/* Heuristics to see if we can skip the conversion.
* Straight from Core Git.
*/
if (filter->attrs.crlf_action == GIT_CRLF_AUTO ||
filter->attrs.crlf_action == GIT_CRLF_GUESS) {
git_text_stats stats;
git_text_gather_stats(&stats, source);
/*
* We're currently not going to even try to convert stuff
* that has bare CR characters. Does anybody do that crazy
* stuff?
*/
if (stats.cr != stats.crlf)
return -1;
/*
* And add some heuristics for binary vs text, of course...
*/
if (git_text_is_binary(&stats))
return -1;
#if 0
if (crlf_action == CRLF_GUESS) {
/*
* If the file in the index has any CR in it, do not convert.
* This is the new safer autocrlf handling.
*/
if (has_cr_in_index(path))
return 0;
}
#endif
if (!stats.cr)
return -1;
}
/* Actually drop the carriage returns */
return drop_crlf(dest, source);
}
int git_filter_add__crlf_to_odb(git_vector *filters, git_repository *repo, const char *path)
{
struct crlf_attrs ca;
struct crlf_filter *filter;
int error;
/* Load gitattributes for the path */
if ((error = crlf_load_attributes(&ca, repo, path)) < 0)
return error;
/*
* Use the core Git logic to see if we should perform CRLF for this file
* based on its attributes & the value of `core.auto_crlf`
*/
ca.crlf_action = crlf_input_action(&ca);
if (ca.crlf_action == GIT_CRLF_BINARY)
return 0;
if (ca.crlf_action == GIT_CRLF_GUESS) {
int auto_crlf;
if ((error = git_repository__cvar(
&auto_crlf, repo, GIT_CVAR_AUTO_CRLF)) < GIT_SUCCESS)
return error;
if (auto_crlf == GIT_AUTO_CRLF_FALSE)
return 0;
}
/* If we're good, we create a new filter object and push it
* into the filters array */
filter = git__malloc(sizeof(struct crlf_filter));
if (filter == NULL)
return GIT_ENOMEM;
filter->f.apply = &crlf_apply_to_odb;
filter->f.do_free = NULL;
memcpy(&filter->attrs, &ca, sizeof(struct crlf_attrs));
return git_vector_insert(filters, filter);
}
......@@ -97,87 +97,77 @@ mode_t git_futils_canonical_mode(mode_t raw_mode)
return 0;
}
int git_futils_readbuffer_updated(git_fbuffer *obj, const char *path, time_t *mtime, int *updated)
int git_futils_readbuffer_updated(git_buf *buf, const char *path, time_t *mtime, int *updated)
{
git_file fd;
size_t len;
struct stat st;
unsigned char *buff;
assert(obj && path && *path);
assert(buf && path && *path);
if (updated != NULL)
*updated = 0;
if (p_stat(path, &st) < 0)
return git__throw(GIT_ENOTFOUND, "Failed to stat file %s", path);
if ((fd = p_open(path, O_RDONLY)) < 0) {
return git__throw(GIT_ENOTFOUND, "Failed to read file '%s': %s", path, strerror(errno));
}
if (S_ISDIR(st.st_mode))
return git__throw(GIT_ERROR, "Can't read a dir into a buffer");
if (p_fstat(fd, &st) < 0 || S_ISDIR(st.st_mode) || !git__is_sizet(st.st_size+1)) {
close(fd);
return git__throw(GIT_EOSERR, "Failed to stat file '%s'", path);
}
/*
* If we were given a time, we only want to read the file if it
* has been modified.
*/
if (mtime != NULL && *mtime >= st.st_mtime)
return GIT_SUCCESS;
if (mtime != NULL && *mtime >= st.st_mtime) {
close(fd);
return 0;
}
if (mtime != NULL)
*mtime = st.st_mtime;
if (!git__is_sizet(st.st_size+1))
return git__throw(GIT_ERROR, "Failed to read file `%s`. An error occured while calculating its size", path);
len = (size_t) st.st_size;
if ((fd = p_open(path, O_RDONLY)) < 0)
return git__throw(GIT_EOSERR, "Failed to open %s for reading", path);
git_buf_clear(buf);
if ((buff = git__malloc(len + 1)) == NULL) {
p_close(fd);
if (git_buf_grow(buf, len + 1) < 0) {
close(fd);
return GIT_ENOMEM;
}
if (p_read(fd, buff, len) < 0) {
p_close(fd);
git__free(buff);
return git__throw(GIT_ERROR, "Failed to read file `%s`", path);
buf->ptr[len] = '\0';
while (len > 0) {
ssize_t read_size = p_read(fd, buf->ptr, len);
if (read_size < 0) {
close(fd);
return git__throw(GIT_EOSERR, "Failed to read from FD");
}
len -= read_size;
buf->size += read_size;
}
buff[len] = '\0';
p_close(fd);
if (mtime != NULL)
*mtime = st.st_mtime;
if (updated != NULL)
*updated = 1;
obj->data = buff;
obj->len = len;
return GIT_SUCCESS;
}
int git_futils_readbuffer(git_fbuffer *obj, const char *path)
{
return git_futils_readbuffer_updated(obj, path, NULL, NULL);
return 0;
}
void git_futils_fbuffer_rtrim(git_fbuffer *obj)
int git_futils_readbuffer(git_buf *buf, const char *path)
{
unsigned char *buff = obj->data;
while (obj->len > 0 && isspace(buff[obj->len - 1]))
obj->len--;
buff[obj->len] = '\0';
return git_futils_readbuffer_updated(buf, path, NULL, NULL);
}
void git_futils_freebuffer(git_fbuffer *obj)
{
assert(obj);
git__free(obj->data);
obj->data = NULL;
}
int git_futils_mv_withpath(const char *from, const char *to, const mode_t dirmode)
{
if (git_futils_mkpath2file(to, dirmode) < GIT_SUCCESS)
......
......@@ -17,17 +17,8 @@
*
* Read whole files into an in-memory buffer for processing
*/
#define GIT_FBUFFER_INIT {NULL, 0}
typedef struct { /* file io buffer */
void *data; /* data bytes */
size_t len; /* data length */
} git_fbuffer;
extern int git_futils_readbuffer(git_fbuffer *obj, const char *path);
extern int git_futils_readbuffer_updated(git_fbuffer *obj, const char *path, time_t *mtime, int *updated);
extern void git_futils_freebuffer(git_fbuffer *obj);
extern void git_futils_fbuffer_rtrim(git_fbuffer *obj);
extern int git_futils_readbuffer(git_buf *obj, const char *path);
extern int git_futils_readbuffer_updated(git_buf *obj, const char *path, time_t *mtime, int *updated);
/**
* File utils
......
/*
* Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#include "common.h"
#include "fileops.h"
#include "hash.h"
#include "filter.h"
#include "repository.h"
#include "git2/config.h"
/* Tweaked from Core Git. I wonder what we could use this for... */
void git_text_gather_stats(git_text_stats *stats, const git_buf *text)
{
size_t i;
memset(stats, 0, sizeof(*stats));
for (i = 0; i < text->size; i++) {
unsigned char c = text->ptr[i];
if (c == '\r') {
stats->cr++;
if (i + 1 < text->size && text->ptr[i + 1] == '\n')
stats->crlf++;
}
else if (c == '\n')
stats->lf++;
else if (c == 0x85)
/* Unicode CR+LF */
stats->crlf++;
else if (c == 127)
/* DEL */
stats->nonprintable++;
else if (c <= 0x1F || (c >= 0x80 && c <= 0x9F)) {
switch (c) {
/* BS, HT, ESC and FF */
case '\b': case '\t': case '\033': case '\014':
stats->printable++;
break;
case 0:
stats->nul++;
/* fall through */
default:
stats->nonprintable++;
}
}
else
stats->printable++;
}
/* If file ends with EOF then don't count this EOF as non-printable. */
if (text->size >= 1 && text->ptr[text->size - 1] == '\032')
stats->nonprintable--;
}
/*
* Fresh from Core Git
*/
int git_text_is_binary(git_text_stats *stats)
{
if (stats->nul)
return 1;
if ((stats->printable >> 7) < stats->nonprintable)
return 1;
/*
* Other heuristics? Average line length might be relevant,
* as might LF vs CR vs CRLF counts..
*
* NOTE! It might be normal to have a low ratio of CRLF to LF
* (somebody starts with a LF-only file and edits it with an editor
* that adds CRLF only to lines that are added..). But do we
* want to support CR-only? Probably not.
*/
return 0;
}
int git_filters_load(git_vector *filters, git_repository *repo, const char *path, int mode)
{
int error;
if (mode == GIT_FILTER_TO_ODB) {
/* Load the CRLF cleanup filter when writing to the ODB */
error = git_filter_add__crlf_to_odb(filters, repo, path);
if (error < GIT_SUCCESS)
return error;
} else {
return git__throw(GIT_ENOTIMPLEMENTED,
"Worktree filters are not implemented yet");
}
return (int)filters->length;
}
void git_filters_free(git_vector *filters)
{
size_t i;
git_filter *filter;
git_vector_foreach(filters, i, filter) {
if (filter->do_free != NULL)
filter->do_free(filter);
else
free(filter);
}
git_vector_free(filters);
}
int git_filters_apply(git_buf *dest, git_buf *source, git_vector *filters)
{
unsigned int i, src;
git_buf *dbuffer[2];
dbuffer[0] = source;
dbuffer[1] = dest;
src = 0;
if (source->size == 0) {
git_buf_clear(dest);
return GIT_SUCCESS;
}
/* Pre-grow the destination buffer to more or less the size
* we expect it to have */
if (git_buf_grow(dest, source->size) < 0)
return GIT_ENOMEM;
for (i = 0; i < filters->length; ++i) {
git_filter *filter = git_vector_get(filters, i);
unsigned int dst = 1 - src;
git_buf_clear(dbuffer[dst]);
/* Apply the filter from dbuffer[src] to the other buffer;
* if the filtering is canceled by the user mid-filter,
* we skip to the next filter without changing the source
* of the double buffering (so that the text goes through
* cleanly).
*/
if (filter->apply(filter, dbuffer[dst], dbuffer[src]) == 0)
src = dst;
if (git_buf_oom(dbuffer[dst]))
return GIT_ENOMEM;
}
/* Ensure that the output ends up in dbuffer[1] (i.e. the dest) */
if (src != 1)
git_buf_swap(dest, source);
return GIT_SUCCESS;
}
/*
* Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#ifndef INCLUDE_filter_h__
#define INCLUDE_filter_h__
#include "common.h"
#include "buffer.h"
#include "git2/odb.h"
#include "git2/repository.h"
typedef struct git_filter {
int (*apply)(struct git_filter *self, git_buf *dest, const git_buf *source);
void (*do_free)(struct git_filter *self);
} git_filter;
typedef enum {
GIT_FILTER_TO_WORKTREE,
GIT_FILTER_TO_ODB
} git_filter_mode;
typedef enum {
GIT_CRLF_GUESS = -1,
GIT_CRLF_BINARY = 0,
GIT_CRLF_TEXT,
GIT_CRLF_INPUT,
GIT_CRLF_CRLF,
GIT_CRLF_AUTO,
} git_crlf_t;
typedef struct {
/* NUL, CR, LF and CRLF counts */
unsigned int nul, cr, lf, crlf;
/* These are just approximations! */
unsigned int printable, nonprintable;
} git_text_stats;
/*
* FILTER API
*/
/*
* For any given path in the working directory, fill the `filters`
* array with the relevant filters that need to be applied.
*
* Mode is either `GIT_FILTER_TO_WORKTREE` if you need to load the
* filters that will be used when checking out a file to the working
* directory, or `GIT_FILTER_TO_ODB` for the filters used when writing
* a file to the ODB.
*
* @param filters Vector where to store all the loaded filters
* @param repo Repository object that contains `path`
* @param path Relative path of the file to be filtered
* @param mode Filtering direction (WT->ODB or ODB->WT)
* @return the number of filters loaded for the file (0 if the file
* doesn't need filtering), or a negative error code
*/
extern int git_filters_load(git_vector *filters, git_repository *repo, const char *path, int mode);
/*
* Apply one or more filters to a file.
*
* The file must have been loaded as a `git_buf` object. Both the `source`
* and `dest` buffers are owned by the caller and must be freed once
* they are no longer needed.
*
* NOTE: Because of the double-buffering schema, the `source` buffer that contains
* the original file may be tampered once the filtering is complete. Regardless,
* the `dest` buffer will always contain the final result of the filtering
*
* @param dest Buffer to store the result of the filtering
* @param source Buffer containing the document to filter
* @param filters A non-empty vector of filters as supplied by `git_filters_load`
* @return GIT_SUCCESS on success, an error code otherwise
*/
extern int git_filters_apply(git_buf *dest, git_buf *source, git_vector *filters);
/*
* Free the `filters` array generated by `git_filters_load`.
*
* Note that this frees both the array and its contents. The array will
* be clean/reusable after this call.
*
* @param filters A filters array as supplied by `git_filters_load`
*/
extern void git_filters_free(git_vector *filters);
/*
* Available filters
*/
/* Strip CRLF, from Worktree to ODB */
extern int git_filter_add__crlf_to_odb(git_vector *filters, git_repository *repo, const char *path);
/*
* PLAINTEXT API
*/
/*
* Gather stats for a piece of text
*
* Fill the `stats` structure with information on the number of
* unreadable characters, carriage returns, etc, so it can be
* used in heuristics.
*/
extern void git_text_gather_stats(git_text_stats *stats, const git_buf *text);
/*
* Process `git_text_stats` data generated by `git_text_stat` to see
* if it qualifies as a binary file
*/
extern int git_text_is_binary(git_text_stats *stats);
#endif
......@@ -11,7 +11,7 @@ static int load_ignore_file(
git_repository *repo, const char *path, git_attr_file *ignores)
{
int error = GIT_SUCCESS;
git_fbuffer fbuf = GIT_FBUFFER_INIT;
git_buf fbuf = GIT_BUF_INIT;
git_attr_fnmatch *match = NULL;
const char *scan = NULL;
char *context = NULL;
......@@ -28,7 +28,7 @@ static int load_ignore_file(
if (error == GIT_SUCCESS)
error = git_futils_readbuffer(&fbuf, path);
scan = fbuf.data;
scan = fbuf.ptr;
while (error == GIT_SUCCESS && *scan) {
if (!match && !(match = git__calloc(1, sizeof(git_attr_fnmatch)))) {
......@@ -53,7 +53,7 @@ static int load_ignore_file(
}
}
git_futils_freebuffer(&fbuf);
git_buf_free(&fbuf);
git__free(match);
git__free(context);
......
......@@ -216,7 +216,7 @@ void git_index_clear(git_index *index)
int git_index_read(git_index *index)
{
int error = GIT_SUCCESS, updated;
git_fbuffer buffer = GIT_FBUFFER_INIT;
git_buf buffer = GIT_BUF_INIT;
time_t mtime;
assert(index->index_file_path);
......@@ -235,12 +235,12 @@ int git_index_read(git_index *index)
if (updated) {
git_index_clear(index);
error = parse_index(index, buffer.data, buffer.len);
error = parse_index(index, buffer.ptr, buffer.size);
if (error == GIT_SUCCESS)
index->last_modified = mtime;
git_futils_freebuffer(&buffer);
git_buf_free(&buffer);
}
if (error < GIT_SUCCESS)
......
......@@ -393,8 +393,8 @@ static int add_default_backends(git_odb *db, const char *objects_dir, int as_alt
static int load_alternates(git_odb *odb, const char *objects_dir)
{
git_buf alternates_path = GIT_BUF_INIT;
git_buf alternates_buf = GIT_BUF_INIT;
char *buffer;
git_fbuffer alternates_buf = GIT_FBUFFER_INIT;
const char *alternate;
int error;
......@@ -412,7 +412,7 @@ static int load_alternates(git_odb *odb, const char *objects_dir)
return git__throw(GIT_EOSERR, "Failed to add backend. Can't read alternates");
}
buffer = (char *)alternates_buf.data;
buffer = (char *)alternates_buf.ptr;
error = GIT_SUCCESS;
/* add each alternate as a new backend; one alternate per line */
......@@ -433,7 +433,8 @@ static int load_alternates(git_odb *odb, const char *objects_dir)
}
git_buf_free(&alternates_path);
git_futils_freebuffer(&alternates_buf);
git_buf_free(&alternates_buf);
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to load alternates");
return error;
......
......@@ -75,13 +75,13 @@ static int object_file_name(git_buf *name, const char *dir, const git_oid *id)
}
static size_t get_binary_object_header(obj_hdr *hdr, git_fbuffer *obj)
static size_t get_binary_object_header(obj_hdr *hdr, git_buf *obj)
{
unsigned char c;
unsigned char *data = obj->data;
unsigned char *data = (unsigned char *)obj->ptr;
size_t shift, size, used = 0;
if (obj->len == 0)
if (obj->size == 0)
return 0;
c = data[used++];
......@@ -90,7 +90,7 @@ static size_t get_binary_object_header(obj_hdr *hdr, git_fbuffer *obj)
size = c & 15;
shift = 4;
while (c & 0x80) {
if (obj->len <= used)
if (obj->size <= used)
return 0;
if (sizeof(size_t) * 8 <= shift)
return 0;
......@@ -177,12 +177,12 @@ static void set_stream_output(z_stream *s, void *out, size_t len)
}
static int start_inflate(z_stream *s, git_fbuffer *obj, void *out, size_t len)
static int start_inflate(z_stream *s, git_buf *obj, void *out, size_t len)
{
int status;
init_stream(s, out, len);
set_stream_input(s, obj->data, obj->len);
set_stream_input(s, obj->ptr, obj->size);
if ((status = inflateInit(s)) < Z_OK)
return status;
......@@ -287,7 +287,7 @@ static void *inflate_tail(z_stream *s, void *hb, size_t used, obj_hdr *hdr)
* of loose object data into packs. This format is no longer used, but
* we must still read it.
*/
static int inflate_packlike_loose_disk_obj(git_rawobj *out, git_fbuffer *obj)
static int inflate_packlike_loose_disk_obj(git_rawobj *out, git_buf *obj)
{
unsigned char *in, *buf;
obj_hdr hdr;
......@@ -310,8 +310,8 @@ static int inflate_packlike_loose_disk_obj(git_rawobj *out, git_fbuffer *obj)
if (!buf)
return GIT_ENOMEM;
in = ((unsigned char *)obj->data) + used;
len = obj->len - used;
in = ((unsigned char *)obj->ptr) + used;
len = obj->size - used;
if (inflate_buffer(in, len, buf, hdr.size)) {
git__free(buf);
return git__throw(GIT_ERROR, "Failed to inflate loose object. Could not inflate buffer");
......@@ -325,7 +325,7 @@ static int inflate_packlike_loose_disk_obj(git_rawobj *out, git_fbuffer *obj)
return GIT_SUCCESS;
}
static int inflate_disk_obj(git_rawobj *out, git_fbuffer *obj)
static int inflate_disk_obj(git_rawobj *out, git_buf *obj)
{
unsigned char head[64], *buf;
z_stream zs;
......@@ -335,7 +335,7 @@ static int inflate_disk_obj(git_rawobj *out, git_fbuffer *obj)
/*
* check for a pack-like loose object
*/
if (!is_zlib_compressed_data(obj->data))
if (!is_zlib_compressed_data((unsigned char *)obj->ptr))
return inflate_packlike_loose_disk_obj(out, obj);
/*
......@@ -383,7 +383,7 @@ static int inflate_disk_obj(git_rawobj *out, git_fbuffer *obj)
static int read_loose(git_rawobj *out, git_buf *loc)
{
int error;
git_fbuffer obj = GIT_FBUFFER_INIT;
git_buf obj = GIT_BUF_INIT;
assert(out && loc);
......@@ -398,7 +398,7 @@ static int read_loose(git_rawobj *out, git_buf *loc)
return git__throw(GIT_ENOTFOUND, "Failed to read loose object. File not found");
error = inflate_disk_obj(out, &obj);
git_futils_freebuffer(&obj);
git_buf_free(&obj);
return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to read loose object");
}
......
......@@ -183,7 +183,7 @@ int git_reflog_read(git_reflog **reflog, git_reference *ref)
{
int error;
git_buf log_path = GIT_BUF_INIT;
git_fbuffer log_file = GIT_FBUFFER_INIT;
git_buf log_file = GIT_BUF_INIT;
git_reflog *log = NULL;
*reflog = NULL;
......@@ -201,7 +201,7 @@ int git_reflog_read(git_reflog **reflog, git_reference *ref)
goto cleanup;
}
if ((error = reflog_parse(log, log_file.data, log_file.len)) < GIT_SUCCESS)
if ((error = reflog_parse(log, log_file.ptr, log_file.size)) < GIT_SUCCESS)
git__rethrow(error, "Failed to read reflog");
else
*reflog = log;
......@@ -209,7 +209,7 @@ int git_reflog_read(git_reflog **reflog, git_reference *ref)
cleanup:
if (error != GIT_SUCCESS && log != NULL)
git_reflog_free(log);
git_futils_freebuffer(&log_file);
git_buf_free(&log_file);
git_buf_free(&log_path);
return error;
......
......@@ -32,15 +32,15 @@ struct packref {
static const int default_table_size = 32;
static int reference_read(
git_fbuffer *file_content,
git_buf *file_content,
time_t *mtime,
const char *repo_path,
const char *ref_name,
int *updated);
/* loose refs */
static int loose_parse_symbolic(git_reference *ref, git_fbuffer *file_content);
static int loose_parse_oid(git_oid *ref, git_fbuffer *file_content);
static int loose_parse_symbolic(git_reference *ref, git_buf *file_content);
static int loose_parse_oid(git_oid *ref, git_buf *file_content);
static int loose_lookup(git_reference *ref);
static int loose_lookup_to_packfile(struct packref **ref_out,
git_repository *repo, const char *name);
......@@ -113,7 +113,7 @@ static int reference_alloc(
return GIT_SUCCESS;
}
static int reference_read(git_fbuffer *file_content, time_t *mtime, const char *repo_path, const char *ref_name, int *updated)
static int reference_read(git_buf *file_content, time_t *mtime, const char *repo_path, const char *ref_name, int *updated)
{
git_buf path = GIT_BUF_INIT;
int error = GIT_SUCCESS;
......@@ -129,15 +129,15 @@ static int reference_read(git_fbuffer *file_content, time_t *mtime, const char *
return error;
}
static int loose_parse_symbolic(git_reference *ref, git_fbuffer *file_content)
static int loose_parse_symbolic(git_reference *ref, git_buf *file_content)
{
const unsigned int header_len = strlen(GIT_SYMREF);
const char *refname_start;
char *eol;
refname_start = (const char *)file_content->data;
refname_start = (const char *)file_content->ptr;
if (file_content->len < (header_len + 1))
if (file_content->size < (header_len + 1))
return git__throw(GIT_EOBJCORRUPTED,
"Failed to parse loose reference. Object too short");
......@@ -165,15 +165,15 @@ static int loose_parse_symbolic(git_reference *ref, git_fbuffer *file_content)
return GIT_SUCCESS;
}
static int loose_parse_oid(git_oid *oid, git_fbuffer *file_content)
static int loose_parse_oid(git_oid *oid, git_buf *file_content)
{
int error;
char *buffer;
buffer = (char *)file_content->data;
buffer = (char *)file_content->ptr;
/* File format: 40 chars (OID) + newline */
if (file_content->len < GIT_OID_HEXSZ + 1)
if (file_content->size < GIT_OID_HEXSZ + 1)
return git__throw(GIT_EOBJCORRUPTED,
"Failed to parse loose reference. Reference too short");
......@@ -193,26 +193,26 @@ static int loose_parse_oid(git_oid *oid, git_fbuffer *file_content)
static git_rtype loose_guess_rtype(const git_buf *full_path)
{
git_fbuffer ref_file = GIT_FBUFFER_INIT;
git_buf ref_file = GIT_BUF_INIT;
git_rtype type;
type = GIT_REF_INVALID;
if (git_futils_readbuffer(&ref_file, full_path->ptr) == GIT_SUCCESS) {
if (git__prefixcmp((const char *)(ref_file.data), GIT_SYMREF) == 0)
if (git__prefixcmp((const char *)(ref_file.ptr), GIT_SYMREF) == 0)
type = GIT_REF_SYMBOLIC;
else
type = GIT_REF_OID;
}
git_futils_freebuffer(&ref_file);
git_buf_free(&ref_file);
return type;
}
static int loose_lookup(git_reference *ref)
{
int error = GIT_SUCCESS, updated;
git_fbuffer ref_file = GIT_FBUFFER_INIT;
git_buf ref_file = GIT_BUF_INIT;
if (reference_read(&ref_file, &ref->mtime,
ref->owner->path_repository, ref->name, &updated) < GIT_SUCCESS)
......@@ -228,7 +228,7 @@ static int loose_lookup(git_reference *ref)
ref->flags = 0;
if (git__prefixcmp((const char *)(ref_file.data), GIT_SYMREF) == 0) {
if (git__prefixcmp((const char *)(ref_file.ptr), GIT_SYMREF) == 0) {
ref->flags |= GIT_REF_SYMBOLIC;
error = loose_parse_symbolic(ref, &ref_file);
} else {
......@@ -236,7 +236,7 @@ static int loose_lookup(git_reference *ref)
error = loose_parse_oid(&ref->target.oid, &ref_file);
}
git_futils_freebuffer(&ref_file);
git_buf_free(&ref_file);
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to lookup loose reference");
......@@ -250,7 +250,7 @@ static int loose_lookup_to_packfile(
const char *name)
{
int error = GIT_SUCCESS;
git_fbuffer ref_file = GIT_FBUFFER_INIT;
git_buf ref_file = GIT_BUF_INIT;
struct packref *ref = NULL;
size_t name_len;
......@@ -273,11 +273,11 @@ static int loose_lookup_to_packfile(
ref->flags = GIT_PACKREF_WAS_LOOSE;
*ref_out = ref;
git_futils_freebuffer(&ref_file);
git_buf_free(&ref_file);
return GIT_SUCCESS;
cleanup:
git_futils_freebuffer(&ref_file);
git_buf_free(&ref_file);
free(ref);
return git__rethrow(error, "Failed to lookup loose reference");
}
......@@ -427,7 +427,7 @@ cleanup:
static int packed_load(git_repository *repo)
{
int error = GIT_SUCCESS, updated;
git_fbuffer packfile = GIT_FBUFFER_INIT;
git_buf packfile = GIT_BUF_INIT;
const char *buffer_start, *buffer_end;
git_refcache *ref_cache = &repo->references;
......@@ -468,8 +468,8 @@ static int packed_load(git_repository *repo)
git_hashtable_clear(ref_cache->packfile);
buffer_start = (const char *)packfile.data;
buffer_end = (const char *)(buffer_start) + packfile.len;
buffer_start = (const char *)packfile.ptr;
buffer_end = (const char *)(buffer_start) + packfile.size;
while (buffer_start < buffer_end && buffer_start[0] == '#') {
buffer_start = strchr(buffer_start, '\n');
......@@ -500,13 +500,13 @@ static int packed_load(git_repository *repo)
}
}
git_futils_freebuffer(&packfile);
git_buf_free(&packfile);
return GIT_SUCCESS;
cleanup:
git_hashtable_free(ref_cache->packfile);
ref_cache->packfile = NULL;
git_futils_freebuffer(&packfile);
git_buf_free(&packfile);
return git__rethrow(error, "Failed to load packed references");
}
......
......@@ -43,6 +43,8 @@ static void drop_config(git_repository *repo)
git_config_free(repo->_config);
repo->_config = NULL;
}
git_repository__cvar_cache_clear(repo);
}
static void drop_index(git_repository *repo)
......@@ -111,6 +113,9 @@ static git_repository *repository_alloc(void)
return NULL;
}
/* set all the entries in the cvar cache to `unset` */
git_repository__cvar_cache_clear(repo);
return repo;
}
......@@ -467,7 +472,7 @@ static int retrieve_ceiling_directories_offset(
*/
static int read_gitfile(git_buf *path_out, const char *file_path, const char *base_path)
{
git_fbuffer file;
git_buf file = GIT_BUF_INIT;
int error;
assert(path_out && file_path);
......@@ -476,22 +481,22 @@ static int read_gitfile(git_buf *path_out, const char *file_path, const char *ba
if (error < GIT_SUCCESS)
return error;
if (git__prefixcmp((char *)file.data, GIT_FILE_CONTENT_PREFIX)) {
git_futils_freebuffer(&file);
if (git__prefixcmp((char *)file.ptr, GIT_FILE_CONTENT_PREFIX)) {
git_buf_free(&file);
return git__throw(GIT_ENOTFOUND, "Invalid gitfile format `%s`", file_path);
}
git_futils_fbuffer_rtrim(&file);
git_buf_rtrim(&file);
if (strlen(GIT_FILE_CONTENT_PREFIX) == file.len) {
git_futils_freebuffer(&file);
if (strlen(GIT_FILE_CONTENT_PREFIX) == file.size) {
git_buf_free(&file);
return git__throw(GIT_ENOTFOUND, "No path in git file `%s`", file_path);
}
error = git_path_prettify_dir(path_out,
((char *)file.data) + strlen(GIT_FILE_CONTENT_PREFIX), base_path);
((char *)file.ptr) + strlen(GIT_FILE_CONTENT_PREFIX), base_path);
git_futils_freebuffer(&file);
git_buf_free(&file);
if (error == GIT_SUCCESS && git_path_exists(path_out->ptr) == 0)
return GIT_SUCCESS;
......
......@@ -26,6 +26,49 @@
#define GIT_DIR_MODE 0755
#define GIT_BARE_DIR_MODE 0777
/** Cvar cache identifiers */
typedef enum {
GIT_CVAR_AUTO_CRLF = 0, /* core.autocrlf */
GIT_CVAR_EOL, /* core.eol */
GIT_CVAR_CACHE_MAX
} git_cvar_cached;
/**
* CVAR value enumerations
*
* These are the values that are actually stored in the cvar cache, instead
* of their string equivalents. These values are internal and symbolic;
* make sure that none of them is set to `-1`, since that is the unique
* identifier for "not cached"
*/
typedef enum {
/* The value hasn't been loaded from the cache yet */
GIT_CVAR_NOT_CACHED = -1,
/* core.safecrlf: false, 'fail', 'warn' */
GIT_SAFE_CRLF_FALSE = 0,
GIT_SAFE_CRLF_FAIL = 1,
GIT_SAFE_CRLF_WARN = 2,
/* core.autocrlf: false, true, 'input; */
GIT_AUTO_CRLF_FALSE = 0,
GIT_AUTO_CRLF_TRUE = 1,
GIT_AUTO_CRLF_INPUT = 2,
GIT_AUTO_CRLF_DEFAULT = GIT_AUTO_CRLF_FALSE,
/* core.eol: unset, 'crlf', 'lf', 'native' */
GIT_EOL_UNSET = 0,
GIT_EOL_CRLF = 1,
GIT_EOL_LF = 2,
#ifdef GIT_WIN32
GIT_EOL_NATIVE = GIT_EOL_CRLF,
#else
GIT_EOL_NATIVE = GIT_EOL_LF,
#endif
GIT_EOL_DEFAULT = GIT_EOL_NATIVE
} git_cvar_value;
/** Base git object for inheritance */
struct git_object {
git_cached_obj cached;
git_repository *repo;
......@@ -46,6 +89,8 @@ struct git_repository {
unsigned is_bare:1;
unsigned int lru_counter;
git_cvar_value cvar_cache[GIT_CVAR_CACHE_MAX];
};
/* fully free the object; internal method, do not
......@@ -55,8 +100,24 @@ void git_object__free(void *object);
int git_oid__parse(git_oid *oid, const char **buffer_out, const char *buffer_end, const char *header);
void git_oid__writebuf(git_buf *buf, const char *header, const git_oid *oid);
/*
* Weak pointers to repository internals.
*
* The returned pointers do not need to be freed. Do not keep
* permanent references to these (i.e. between API calls), since they may
* become invalidated if the user replaces a repository internal.
*/
int git_repository_config__weakptr(git_config **out, git_repository *repo);
int git_repository_odb__weakptr(git_odb **out, git_repository *repo);
int git_repository_index__weakptr(git_index **out, git_repository *repo);
/*
* CVAR cache
*
* Efficient access to the most used config variables of a repository.
* The cache is cleared everytime the config backend is replaced.
*/
int git_repository__cvar(int *out, git_repository *repo, git_cvar_cached cvar);
void git_repository__cvar_cache_clear(git_repository *repo);
#endif
#ifndef __CLAR_TEST_ATTR_EXPECT__
#define __CLAR_TEST_ATTR_EXPECT__
enum attr_expect_t {
EXPECT_FALSE,
EXPECT_TRUE,
EXPECT_UNDEFINED,
EXPECT_STRING
};
struct attr_expected {
const char *path;
const char *attr;
enum attr_expect_t expected;
const char *expected_str;
};
static inline void attr_check_expected(
enum attr_expect_t expected,
const char *expected_str,
const char *value)
{
switch (expected) {
case EXPECT_TRUE:
cl_assert(GIT_ATTR_TRUE(value));
break;
case EXPECT_FALSE:
cl_assert(GIT_ATTR_FALSE(value));
break;
case EXPECT_UNDEFINED:
cl_assert(GIT_ATTR_UNSPECIFIED(value));
break;
case EXPECT_STRING:
cl_assert_strequal(expected_str, value);
break;
}
}
#endif
#include "clar_libgit2.h"
#include "attr_file.h"
#include "attr_expect.h"
#define get_rule(X) ((git_attr_rule *)git_vector_get(&file->rules,(X)))
#define get_assign(R,Y) ((git_attr_assignment *)git_vector_get(&(R)->assigns,(Y)))
......@@ -25,7 +26,7 @@ void test_attr_file__simple_read(void)
assign = get_assign(rule, 0);
cl_assert(assign != NULL);
cl_assert_strequal("binary", assign->name);
cl_assert(assign->value == GIT_ATTR_TRUE);
cl_assert(GIT_ATTR_TRUE(assign->value));
cl_assert(!assign->is_allocated);
git_attr_file__free(file);
......@@ -54,7 +55,7 @@ void test_attr_file__match_variants(void)
assign = get_assign(rule,0);
cl_assert_strequal("attr0", assign->name);
cl_assert(assign->name_hash == git_attr_file__name_hash(assign->name));
cl_assert(assign->value == GIT_ATTR_TRUE);
cl_assert(GIT_ATTR_TRUE(assign->value));
cl_assert(!assign->is_allocated);
rule = get_rule(1);
......@@ -83,7 +84,7 @@ void test_attr_file__match_variants(void)
cl_assert(rule->assigns.length == 1);
assign = get_assign(rule,0);
cl_assert_strequal("attr7", assign->name);
cl_assert(assign->value == GIT_ATTR_TRUE);
cl_assert(GIT_ATTR_TRUE(assign->value));
rule = get_rule(8);
cl_assert_strequal("pat8 with spaces", rule->match.pattern);
......@@ -102,8 +103,8 @@ static void check_one_assign(
int assign_idx,
const char *pattern,
const char *name,
const char *value,
int is_allocated)
enum attr_expect_t expected,
const char *expected_str)
{
git_attr_rule *rule = get_rule(rule_idx);
git_attr_assignment *assign = get_assign(rule, assign_idx);
......@@ -112,11 +113,8 @@ static void check_one_assign(
cl_assert(rule->assigns.length == 1);
cl_assert_strequal(name, assign->name);
cl_assert(assign->name_hash == git_attr_file__name_hash(assign->name));
cl_assert(assign->is_allocated == is_allocated);
if (is_allocated)
cl_assert_strequal(value, assign->value);
else
cl_assert(assign->value == value);
attr_check_expected(expected, expected_str, assign->value);
}
void test_attr_file__assign_variants(void)
......@@ -130,14 +128,14 @@ void test_attr_file__assign_variants(void)
cl_assert_strequal(cl_fixture("attr/attr2"), file->path);
cl_assert(file->rules.length == 11);
check_one_assign(file, 0, 0, "pat0", "simple", GIT_ATTR_TRUE, 0);
check_one_assign(file, 1, 0, "pat1", "neg", GIT_ATTR_FALSE, 0);
check_one_assign(file, 2, 0, "*", "notundef", GIT_ATTR_TRUE, 0);
check_one_assign(file, 3, 0, "pat2", "notundef", NULL, 0);
check_one_assign(file, 4, 0, "pat3", "assigned", "test-value", 1);
check_one_assign(file, 5, 0, "pat4", "rule-with-more-chars", "value-with-more-chars", 1);
check_one_assign(file, 6, 0, "pat5", "empty", GIT_ATTR_TRUE, 0);
check_one_assign(file, 7, 0, "pat6", "negempty", GIT_ATTR_FALSE, 0);
check_one_assign(file, 0, 0, "pat0", "simple", EXPECT_TRUE, NULL);
check_one_assign(file, 1, 0, "pat1", "neg", EXPECT_FALSE, NULL);
check_one_assign(file, 2, 0, "*", "notundef", EXPECT_TRUE, NULL);
check_one_assign(file, 3, 0, "pat2", "notundef", EXPECT_UNDEFINED, NULL);
check_one_assign(file, 4, 0, "pat3", "assigned", EXPECT_STRING, "test-value");
check_one_assign(file, 5, 0, "pat4", "rule-with-more-chars", EXPECT_STRING, "value-with-more-chars");
check_one_assign(file, 6, 0, "pat5", "empty", EXPECT_TRUE, NULL);
check_one_assign(file, 7, 0, "pat6", "negempty", EXPECT_FALSE, NULL);
rule = get_rule(8);
cl_assert_strequal("pat7", rule->match.pattern);
......@@ -148,11 +146,11 @@ void test_attr_file__assign_variants(void)
assign = git_attr_rule__lookup_assignment(rule, "multiple");
cl_assert(assign);
cl_assert_strequal("multiple", assign->name);
cl_assert(assign->value == GIT_ATTR_TRUE);
cl_assert(GIT_ATTR_TRUE(assign->value));
assign = git_attr_rule__lookup_assignment(rule, "single");
cl_assert(assign);
cl_assert_strequal("single", assign->name);
cl_assert(assign->value == GIT_ATTR_FALSE);
cl_assert(GIT_ATTR_FALSE(assign->value));
assign = git_attr_rule__lookup_assignment(rule, "values");
cl_assert(assign);
cl_assert_strequal("values", assign->name);
......@@ -174,13 +172,13 @@ void test_attr_file__assign_variants(void)
assign = git_attr_rule__lookup_assignment(rule, "again");
cl_assert(assign);
cl_assert_strequal("again", assign->name);
cl_assert(assign->value == GIT_ATTR_TRUE);
cl_assert(GIT_ATTR_TRUE(assign->value));
assign = git_attr_rule__lookup_assignment(rule, "another");
cl_assert(assign);
cl_assert_strequal("another", assign->name);
cl_assert_strequal("12321", assign->value);
check_one_assign(file, 10, 0, "pat9", "at-eof", GIT_ATTR_FALSE, 0);
check_one_assign(file, 10, 0, "pat9", "at-eof", EXPECT_FALSE, NULL);
git_attr_file__free(file);
}
......@@ -204,10 +202,10 @@ void test_attr_file__check_attr_examples(void)
cl_assert_strequal("java", assign->value);
assign = git_attr_rule__lookup_assignment(rule, "crlf");
cl_assert_strequal("crlf", assign->name);
cl_assert(GIT_ATTR_FALSE == assign->value);
cl_assert(GIT_ATTR_FALSE(assign->value));
assign = git_attr_rule__lookup_assignment(rule, "myAttr");
cl_assert_strequal("myAttr", assign->name);
cl_assert(GIT_ATTR_TRUE == assign->value);
cl_assert(GIT_ATTR_TRUE(assign->value));
assign = git_attr_rule__lookup_assignment(rule, "missing");
cl_assert(assign == NULL);
......
......@@ -3,6 +3,8 @@
#include "git2/attr.h"
#include "attr.h"
#include "attr_expect.h"
static git_repository *g_repo = NULL;
void test_attr_repo__initialize(void)
......@@ -28,67 +30,45 @@ void test_attr_repo__cleanup(void)
void test_attr_repo__get_one(void)
{
const char *value;
struct {
const char *file;
const char *attr;
const char *expected;
} test_cases[] = {
{ "root_test1", "repoattr", GIT_ATTR_TRUE },
{ "root_test1", "rootattr", GIT_ATTR_TRUE },
{ "root_test1", "missingattr", NULL },
{ "root_test1", "subattr", NULL },
{ "root_test1", "negattr", NULL },
{ "root_test2", "repoattr", GIT_ATTR_TRUE },
{ "root_test2", "rootattr", GIT_ATTR_FALSE },
{ "root_test2", "missingattr", NULL },
{ "root_test2", "multiattr", GIT_ATTR_FALSE },
{ "root_test3", "repoattr", GIT_ATTR_TRUE },
{ "root_test3", "rootattr", NULL },
{ "root_test3", "multiattr", "3" },
{ "root_test3", "multi2", NULL },
{ "sub/subdir_test1", "repoattr", GIT_ATTR_TRUE },
{ "sub/subdir_test1", "rootattr", GIT_ATTR_TRUE },
{ "sub/subdir_test1", "missingattr", NULL },
{ "sub/subdir_test1", "subattr", "yes" },
{ "sub/subdir_test1", "negattr", GIT_ATTR_FALSE },
{ "sub/subdir_test1", "another", NULL },
{ "sub/subdir_test2.txt", "repoattr", GIT_ATTR_TRUE },
{ "sub/subdir_test2.txt", "rootattr", GIT_ATTR_TRUE },
{ "sub/subdir_test2.txt", "missingattr", NULL },
{ "sub/subdir_test2.txt", "subattr", "yes" },
{ "sub/subdir_test2.txt", "negattr", GIT_ATTR_FALSE },
{ "sub/subdir_test2.txt", "another", "zero" },
{ "sub/subdir_test2.txt", "reposub", GIT_ATTR_TRUE },
{ "sub/sub/subdir.txt", "another", "one" },
{ "sub/sub/subdir.txt", "reposubsub", GIT_ATTR_TRUE },
{ "sub/sub/subdir.txt", "reposub", NULL },
{ "does-not-exist", "foo", "yes" },
{ "sub/deep/file", "deepdeep", GIT_ATTR_TRUE },
{ NULL, NULL, NULL }
}, *scan;
for (scan = test_cases; scan->file != NULL; scan++) {
git_buf b = GIT_BUF_INIT;
git_buf_printf(&b, "%s:%s == expect %s",
scan->file, scan->attr, scan->expected);
cl_must_pass_(
git_attr_get(g_repo, scan->file, scan->attr, &value) == GIT_SUCCESS,
b.ptr);
git_buf_printf(&b, ", got %s", value);
if (scan->expected == NULL ||
scan->expected == GIT_ATTR_TRUE ||
scan->expected == GIT_ATTR_FALSE)
{
cl_assert_(scan->expected == value, b.ptr);
} else {
cl_assert_strequal(scan->expected, value);
}
struct attr_expected test_cases[] = {
{ "root_test1", "repoattr", EXPECT_TRUE, NULL },
{ "root_test1", "rootattr", EXPECT_TRUE, NULL },
{ "root_test1", "missingattr", EXPECT_UNDEFINED, NULL },
{ "root_test1", "subattr", EXPECT_UNDEFINED, NULL },
{ "root_test1", "negattr", EXPECT_UNDEFINED, NULL },
{ "root_test2", "repoattr", EXPECT_TRUE, NULL },
{ "root_test2", "rootattr", EXPECT_FALSE, NULL },
{ "root_test2", "missingattr", EXPECT_UNDEFINED, NULL },
{ "root_test2", "multiattr", EXPECT_FALSE, NULL },
{ "root_test3", "repoattr", EXPECT_TRUE, NULL },
{ "root_test3", "rootattr", EXPECT_UNDEFINED, NULL },
{ "root_test3", "multiattr", EXPECT_STRING, "3" },
{ "root_test3", "multi2", EXPECT_UNDEFINED, NULL },
{ "sub/subdir_test1", "repoattr", EXPECT_TRUE, NULL },
{ "sub/subdir_test1", "rootattr", EXPECT_TRUE, NULL },
{ "sub/subdir_test1", "missingattr", EXPECT_UNDEFINED, NULL },
{ "sub/subdir_test1", "subattr", EXPECT_STRING, "yes" },
{ "sub/subdir_test1", "negattr", EXPECT_FALSE, NULL },
{ "sub/subdir_test1", "another", EXPECT_UNDEFINED, NULL },
{ "sub/subdir_test2.txt", "repoattr", EXPECT_TRUE, NULL },
{ "sub/subdir_test2.txt", "rootattr", EXPECT_TRUE, NULL },
{ "sub/subdir_test2.txt", "missingattr", EXPECT_UNDEFINED, NULL },
{ "sub/subdir_test2.txt", "subattr", EXPECT_STRING, "yes" },
{ "sub/subdir_test2.txt", "negattr", EXPECT_FALSE, NULL },
{ "sub/subdir_test2.txt", "another", EXPECT_STRING, "zero" },
{ "sub/subdir_test2.txt", "reposub", EXPECT_TRUE, NULL },
{ "sub/sub/subdir.txt", "another", EXPECT_STRING, "one" },
{ "sub/sub/subdir.txt", "reposubsub", EXPECT_TRUE, NULL },
{ "sub/sub/subdir.txt", "reposub", EXPECT_UNDEFINED, NULL },
{ "does-not-exist", "foo", EXPECT_STRING, "yes" },
{ "sub/deep/file", "deepdeep", EXPECT_TRUE, NULL },
{ NULL, NULL, 0, NULL }
}, *scan;
git_buf_free(&b);
for (scan = test_cases; scan->path != NULL; scan++) {
cl_git_pass(git_attr_get(g_repo, scan->path, scan->attr, &value));
attr_check_expected(scan->expected, scan->expected_str, value);
}
cl_git_pass(git_attr_cache__is_cached(g_repo, ".git/info/attributes"));
......@@ -103,25 +83,24 @@ void test_attr_repo__get_many(void)
cl_git_pass(git_attr_get_many(g_repo, "root_test1", 4, names, values));
cl_assert(values[0] == GIT_ATTR_TRUE);
cl_assert(values[1] == GIT_ATTR_TRUE);
cl_assert(values[2] == NULL);
cl_assert(values[3] == NULL);
cl_assert(GIT_ATTR_TRUE(values[0]));
cl_assert(GIT_ATTR_TRUE(values[1]));
cl_assert(GIT_ATTR_UNSPECIFIED(values[2]));
cl_assert(GIT_ATTR_UNSPECIFIED(values[3]));
cl_git_pass(git_attr_get_many(g_repo, "root_test2", 4, names, values));
cl_assert(values[0] == GIT_ATTR_TRUE);
cl_assert(values[1] == GIT_ATTR_FALSE);
cl_assert(values[2] == NULL);
cl_assert(values[3] == NULL);
cl_assert(GIT_ATTR_TRUE(values[0]));
cl_assert(GIT_ATTR_FALSE(values[1]));
cl_assert(GIT_ATTR_UNSPECIFIED(values[2]));
cl_assert(GIT_ATTR_UNSPECIFIED(values[3]));
cl_git_pass(git_attr_get_many(g_repo, "sub/subdir_test1", 4, names, values));
cl_assert(values[0] == GIT_ATTR_TRUE);
cl_assert(values[1] == GIT_ATTR_TRUE);
cl_assert(values[2] == NULL);
cl_assert(GIT_ATTR_TRUE(values[0]));
cl_assert(GIT_ATTR_TRUE(values[1]));
cl_assert(GIT_ATTR_UNSPECIFIED(values[2]));
cl_assert_strequal("yes", values[3]);
}
static int count_attrs(
......@@ -161,19 +140,19 @@ void test_attr_repo__manpage_example(void)
const char *value;
cl_git_pass(git_attr_get(g_repo, "sub/abc", "foo", &value));
cl_assert(value == GIT_ATTR_TRUE);
cl_assert(GIT_ATTR_TRUE(value));
cl_git_pass(git_attr_get(g_repo, "sub/abc", "bar", &value));
cl_assert(value == NULL);
cl_assert(GIT_ATTR_UNSPECIFIED(value));
cl_git_pass(git_attr_get(g_repo, "sub/abc", "baz", &value));
cl_assert(value == GIT_ATTR_FALSE);
cl_assert(GIT_ATTR_FALSE(value));
cl_git_pass(git_attr_get(g_repo, "sub/abc", "merge", &value));
cl_assert_strequal("filfre", value);
cl_git_pass(git_attr_get(g_repo, "sub/abc", "frotz", &value));
cl_assert(value == NULL);
cl_assert(GIT_ATTR_UNSPECIFIED(value));
}
void test_attr_repo__macros(void)
......@@ -185,24 +164,24 @@ void test_attr_repo__macros(void)
cl_git_pass(git_attr_get_many(g_repo, "binfile", 5, names, values));
cl_assert(values[0] == GIT_ATTR_TRUE);
cl_assert(values[1] == GIT_ATTR_TRUE);
cl_assert(values[2] == GIT_ATTR_FALSE);
cl_assert(values[3] == GIT_ATTR_FALSE);
cl_assert(values[4] == NULL);
cl_assert(GIT_ATTR_TRUE(values[0]));
cl_assert(GIT_ATTR_TRUE(values[1]));
cl_assert(GIT_ATTR_FALSE(values[2]));
cl_assert(GIT_ATTR_FALSE(values[3]));
cl_assert(GIT_ATTR_UNSPECIFIED(values[4]));
cl_git_pass(git_attr_get_many(g_repo, "macro_test", 5, names2, values));
cl_assert(values[0] == GIT_ATTR_TRUE);
cl_assert(values[1] == GIT_ATTR_TRUE);
cl_assert(values[2] == GIT_ATTR_FALSE);
cl_assert(values[3] == NULL);
cl_assert(GIT_ATTR_TRUE(values[0]));
cl_assert(GIT_ATTR_TRUE(values[1]));
cl_assert(GIT_ATTR_FALSE(values[2]));
cl_assert(GIT_ATTR_UNSPECIFIED(values[3]));
cl_assert_strequal("77", values[4]);
cl_git_pass(git_attr_get_many(g_repo, "macro_test", 3, names3, values));
cl_assert(values[0] == GIT_ATTR_TRUE);
cl_assert(values[1] == GIT_ATTR_FALSE);
cl_assert(GIT_ATTR_TRUE(values[0]));
cl_assert(GIT_ATTR_FALSE(values[1]));
cl_assert_strequal("answer", values[2]);
}
......@@ -215,9 +194,9 @@ void test_attr_repo__bad_macros(void)
cl_git_pass(git_attr_get_many(g_repo, "macro_bad", 6, names, values));
/* these three just confirm that the "mymacro" rule ran */
cl_assert(values[0] == NULL);
cl_assert(values[1] == GIT_ATTR_TRUE);
cl_assert(values[2] == GIT_ATTR_FALSE);
cl_assert(GIT_ATTR_UNSPECIFIED(values[0]));
cl_assert(GIT_ATTR_TRUE(values[1]));
cl_assert(GIT_ATTR_FALSE(values[2]));
/* file contains:
* # let's try some malicious macro defs
......@@ -241,7 +220,7 @@ void test_attr_repo__bad_macros(void)
* so summary results should be:
* -firstmacro secondmacro="hahaha" thirdmacro
*/
cl_assert(values[3] == GIT_ATTR_FALSE);
cl_assert(GIT_ATTR_FALSE(values[3]));
cl_assert_strequal("hahaha", values[4]);
cl_assert(values[5] == GIT_ATTR_TRUE);
cl_assert(GIT_ATTR_TRUE(values[5]));
}
......@@ -27,3 +27,15 @@ void cl_git_mkfile(const char *filename, const char *content)
cl_must_pass(p_close(fd));
}
void cl_git_append2file(const char *filename, const char *new_content)
{
int fd = p_open(filename, O_WRONLY | O_APPEND | O_CREAT);
cl_assert(fd != 0);
if (!new_content)
new_content = "\n";
cl_must_pass(p_write(fd, new_content, strlen(new_content)));
cl_must_pass(p_close(fd));
cl_must_pass(p_chmod(filename, 0644));
}
......@@ -53,5 +53,6 @@ GIT_INLINE(void) cl_assert_strequal_internal(
/* Write the contents of a buffer to disk */
void cl_git_mkfile(const char *filename, const char *content);
void cl_git_append2file(const char *filename, const char *new_content);
#endif
......@@ -218,8 +218,8 @@ check_buf_append(
const char* data_a,
const char* data_b,
const char* expected_data,
ssize_t expected_size,
ssize_t expected_asize)
size_t expected_size,
size_t expected_asize)
{
git_buf tgt = GIT_BUF_INIT;
......@@ -371,8 +371,8 @@ void test_core_buffer__7(void)
git_buf_attach(&a, b, 0);
cl_assert_strequal(fun, a.ptr);
cl_assert(a.size == (ssize_t)strlen(fun));
cl_assert(a.asize == (ssize_t)strlen(fun) + 1);
cl_assert(a.size == strlen(fun));
cl_assert(a.asize == strlen(fun) + 1);
git_buf_free(&a);
......@@ -380,8 +380,8 @@ void test_core_buffer__7(void)
git_buf_attach(&a, b, strlen(fun) + 1);
cl_assert_strequal(fun, a.ptr);
cl_assert(a.size == (ssize_t)strlen(fun));
cl_assert(a.asize == (ssize_t)strlen(fun) + 1);
cl_assert(a.size == strlen(fun));
cl_assert(a.asize == strlen(fun) + 1);
git_buf_free(&a);
}
......
......@@ -243,7 +243,7 @@ void test_core_path__07_path_to_dir(void)
void test_core_path__08_self_join(void)
{
git_buf path = GIT_BUF_INIT;
ssize_t asize = 0;
size_t asize = 0;
asize = path.asize;
cl_git_pass(git_buf_sets(&path, "/foo"));
......
#include "clar_libgit2.h"
#include "posix.h"
#include "blob.h"
#include "filter.h"
static git_repository *g_repo = NULL;
#define NUM_TEST_OBJECTS 6
static git_oid g_oids[NUM_TEST_OBJECTS];
static const char *g_raw[NUM_TEST_OBJECTS] = {
"",
"foo\nbar\n",
"foo\rbar\r",
"foo\r\nbar\r\n",
"foo\nbar\rboth\r\nreversed\n\ragain\nproblems\r",
"123\n\000\001\002\003\004abc\255\254\253\r\n"
};
static int g_len[NUM_TEST_OBJECTS] = { -1, -1, -1, -1, -1, 17 };
static git_text_stats g_stats[NUM_TEST_OBJECTS] = {
{ 0, 0, 0, 0, 0, 0 },
{ 0, 0, 2, 0, 6, 0 },
{ 0, 2, 0, 0, 6, 0 },
{ 0, 2, 2, 2, 6, 0 },
{ 0, 4, 4, 1, 31, 0 },
{ 1, 1, 2, 1, 9, 5 }
};
static git_buf g_crlf_filtered[NUM_TEST_OBJECTS] = {
{ "", 0, 0 },
{ "foo\nbar\n", 0, 8 },
{ "foo\rbar\r", 0, 8 },
{ "foo\nbar\n", 0, 8 },
{ "foo\nbar\rboth\nreversed\n\ragain\nproblems\r", 0, 38 },
{ "123\n\000\001\002\003\004abc\255\254\253\n", 0, 16 }
};
void test_object_blob_filter__initialize(void)
{
int i;
cl_fixture_sandbox("empty_standard_repo");
cl_git_pass(p_rename(
"empty_standard_repo/.gitted", "empty_standard_repo/.git"));
cl_git_pass(git_repository_open(&g_repo, "empty_standard_repo"));
for (i = 0; i < NUM_TEST_OBJECTS; i++) {
size_t len = (g_len[i] < 0) ? strlen(g_raw[i]) : (size_t)g_len[i];
g_len[i] = (int)len;
cl_git_pass(
git_blob_create_frombuffer(&g_oids[i], g_repo, g_raw[i], len)
);
}
}
void test_object_blob_filter__cleanup(void)
{
git_repository_free(g_repo);
g_repo = NULL;
cl_fixture_cleanup("empty_standard_repo");
}
void test_object_blob_filter__unfiltered(void)
{
int i;
git_blob *blob;
for (i = 0; i < NUM_TEST_OBJECTS; i++) {
cl_git_pass(git_blob_lookup(&blob, g_repo, &g_oids[i]));
cl_assert((size_t)g_len[i] == git_blob_rawsize(blob));
cl_assert(memcmp(git_blob_rawcontent(blob), g_raw[i], g_len[i]) == 0);
git_blob_free(blob);
}
}
void test_object_blob_filter__stats(void)
{
int i;
git_blob *blob;
git_buf buf = GIT_BUF_INIT;
git_text_stats stats;
for (i = 0; i < NUM_TEST_OBJECTS; i++) {
cl_git_pass(git_blob_lookup(&blob, g_repo, &g_oids[i]));
cl_git_pass(git_blob__getbuf(&buf, blob));
git_text_gather_stats(&stats, &buf);
cl_assert(memcmp(&g_stats[i], &stats, sizeof(stats)) == 0);
git_blob_free(blob);
}
git_buf_free(&buf);
}
void test_object_blob_filter__to_odb(void)
{
git_vector filters = GIT_VECTOR_INIT;
git_config *cfg;
int i;
git_blob *blob;
git_buf orig = GIT_BUF_INIT, out = GIT_BUF_INIT;
cl_git_pass(git_repository_config(&cfg, g_repo));
cl_assert(cfg);
git_attr_cache_flush(g_repo);
cl_git_append2file("empty_standard_repo/.gitattributes", "*.txt text\n");
cl_assert(git_filters_load(
&filters, g_repo, "filename.txt", GIT_FILTER_TO_ODB) > 0);
cl_assert(filters.length == 1);
for (i = 0; i < NUM_TEST_OBJECTS; i++) {
cl_git_pass(git_blob_lookup(&blob, g_repo, &g_oids[i]));
cl_git_pass(git_blob__getbuf(&orig, blob));
cl_git_pass(git_filters_apply(&out, &orig, &filters));
cl_assert(git_buf_cmp(&out, &g_crlf_filtered[i]) == 0);
git_blob_free(blob);
}
git_filters_free(&filters);
git_buf_free(&orig);
git_buf_free(&out);
git_config_free(cfg);
}
......@@ -182,7 +182,7 @@ int cmp_objects(git_rawobj *o, object_data *d)
int copy_file(const char *src, const char *dst)
{
git_fbuffer source_buf;
git_buf source_buf = GIT_BUF_INIT;
git_file dst_fd;
int error = GIT_ERROR;
......@@ -193,10 +193,10 @@ int copy_file(const char *src, const char *dst)
if (dst_fd < 0)
goto cleanup;
error = p_write(dst_fd, source_buf.data, source_buf.len);
error = p_write(dst_fd, source_buf.ptr, source_buf.size);
cleanup:
git_futils_freebuffer(&source_buf);
git_buf_free(&source_buf);
p_close(dst_fd);
return error;
......@@ -204,22 +204,23 @@ cleanup:
int cmp_files(const char *a, const char *b)
{
git_fbuffer buf_a, buf_b;
git_buf buf_a = GIT_BUF_INIT;
git_buf buf_b = GIT_BUF_INIT;
int error = GIT_ERROR;
if (git_futils_readbuffer(&buf_a, a) < GIT_SUCCESS)
return GIT_ERROR;
if (git_futils_readbuffer(&buf_b, b) < GIT_SUCCESS) {
git_futils_freebuffer(&buf_a);
git_buf_free(&buf_a);
return GIT_ERROR;
}
if (buf_a.len == buf_b.len && !memcmp(buf_a.data, buf_b.data, buf_a.len))
if (buf_a.size == buf_b.size && !memcmp(buf_a.ptr, buf_b.ptr, buf_a.size))
error = GIT_SUCCESS;
git_futils_freebuffer(&buf_a);
git_futils_freebuffer(&buf_b);
git_buf_free(&buf_a);
git_buf_free(&buf_b);
return error;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment