<commit_before>#include "cache.h"
#include "attr.h"
#include "run-command.h"

/*
 * convert.c - convert a file when checking it out and checking it in.
 *
 * This should use the pathname to decide on whether it wants to do some
 * more interesting conversions (automatic gzip/unzip, general format
 * conversions etc etc), but by default it just does automatic CRLF<->LF
 * translation when the "auto_crlf" option is set.
 */

#define CRLF_GUESS	(-1)
#define CRLF_BINARY	0
#define CRLF_TEXT	1
#define CRLF_INPUT	2

struct text_stat {
	/* CR, LF and CRLF counts */
	unsigned cr, lf, crlf;

	/* These are just approximations! */
	unsigned printable, nonprintable;
};

static void gather_stats(const char *buf, unsigned long size, struct text_stat *stats)
{
	unsigned long i;

	memset(stats, 0, sizeof(*stats));

	for (i = 0; i < size; i++) {
		unsigned char c = buf[i];
		if (c == '\r') {
			stats->cr++;
			if (i+1 < size && buf[i+1] == '\n')
				stats->crlf++;
			continue;
		}
		if (c == '\n') {
			stats->lf++;
			continue;
		}
		if (c == 127)
			/* DEL */
			stats->nonprintable++;
		else if (c < 32) {
			switch (c) {
				/* BS, HT, ESC and FF */
			case '\b': case '\t': case '\033': case '\014':
				stats->printable++;
				break;
			default:
				stats->nonprintable++;
			}
		}
		else
			stats->printable++;
	}
}

/*
 * The same heuristics as diff.c::mmfile_is_binary()
 */
static int is_binary(unsigned long size, struct text_stat *stats)
{

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

static char *crlf_to_git(const char *path, const char *src, unsigned long *sizep, int action)
{
	char *buffer, *dst;
	unsigned long size, nsize;
	struct text_stat stats;

	if ((action == CRLF_BINARY) || (action == CRLF_GUESS && !auto_crlf))
		return NULL;

	size = *sizep;
	if (!size)
		return NULL;

	gather_stats(src, size, &stats);

	/* No CR? Nothing to convert, regardless. */
	if (!stats.cr)
		return NULL;

	if (action == CRLF_GUESS) {
		/*
		 * 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 NULL;

		/*
		 * And add some heuristics for binary vs text, of course...
		 */
		if (is_binary(size, &stats))
			return NULL;
	}

	/*
	 * Ok, allocate a new buffer, fill it in, and return it
	 * to let the caller know that we switched buffers.
	 */
	nsize = size - stats.crlf;
	buffer = xmalloc(nsize);
	*sizep = nsize;

	dst = buffer;
	if (action == CRLF_GUESS) {
		/*
		 * If we guessed, we already know we rejected a file with
		 * lone CR, and we can strip a CR without looking at what
		 * follow it.
		 */
		do {
			unsigned char c = *src++;
			if (c != '\r')
				*dst++ = c;
		} while (--size);
	} else {
		do {
			unsigned char c = *src++;
			if (! (c == '\r' && (1 < size && *src == '\n')))
				*dst++ = c;
		} while (--size);
	}

	return buffer;
}

static char *crlf_to_worktree(const char *path, const char *src, unsigned long *sizep, int action)
{
	char *buffer, *dst;
	unsigned long size, nsize;
	struct text_stat stats;
	unsigned char last;

	if ((action == CRLF_BINARY) || (action == CRLF_INPUT) ||
	    (action == CRLF_GUESS && auto_crlf <= 0))
		return NULL;

	size = *sizep;
	if (!size)
		return NULL;

	gather_stats(src, size, &stats);

	/* No LF? Nothing to convert, regardless. */
	if (!stats.lf)
		return NULL;

	/* Was it already in CRLF format? */<commit_msg>Fix crlf attribute handling to match documentation<commit_after>#include "cache.h"
#include "attr.h"
#include "run-command.h"

/*
 * convert.c - convert a file when checking it out and checking it in.
 *
 * This should use the pathname to decide on whether it wants to do some
 * more interesting conversions (automatic gzip/unzip, general format
 * conversions etc etc), but by default it just does automatic CRLF<->LF
 * translation when the "auto_crlf" option is set.
 */

#define CRLF_GUESS	(-1)
#define CRLF_BINARY	0
#define CRLF_TEXT	1
#define CRLF_INPUT	2

struct text_stat {
	/* CR, LF and CRLF counts */
	unsigned cr, lf, crlf;

	/* These are just approximations! */
	unsigned printable, nonprintable;
};

static void gather_stats(const char *buf, unsigned long size, struct text_stat *stats)
{
	unsigned long i;

	memset(stats, 0, sizeof(*stats));

	for (i = 0; i < size; i++) {
		unsigned char c = buf[i];
		if (c == '\r') {
			stats->cr++;
			if (i+1 < size && buf[i+1] == '\n')
				stats->crlf++;
			continue;
		}
		if (c == '\n') {
			stats->lf++;
			continue;
		}
		if (c == 127)
			/* DEL */
			stats->nonprintable++;
		else if (c < 32) {
			switch (c) {
				/* BS, HT, ESC and FF */
			case '\b': case '\t': case '\033': case '\014':
				stats->printable++;
				break;
			default:
				stats->nonprintable++;
			}
		}
		else
			stats->printable++;
	}
}

/*
 * The same heuristics as diff.c::mmfile_is_binary()
 */
static int is_binary(unsigned long size, struct text_stat *stats)
{

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

static char *crlf_to_git(const char *path, const char *src, unsigned long *sizep, int action)
{
	char *buffer, *dst;
	unsigned long size, nsize;
	struct text_stat stats;

	if ((action == CRLF_BINARY) || !auto_crlf)
		return NULL;

	size = *sizep;
	if (!size)
		return NULL;

	gather_stats(src, size, &stats);

	/* No CR? Nothing to convert, regardless. */
	if (!stats.cr)
		return NULL;

	if (action == CRLF_GUESS) {
		/*
		 * 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 NULL;

		/*
		 * And add some heuristics for binary vs text, of course...
		 */
		if (is_binary(size, &stats))
			return NULL;
	}

	/*
	 * Ok, allocate a new buffer, fill it in, and return it
	 * to let the caller know that we switched buffers.
	 */
	nsize = size - stats.crlf;
	buffer = xmalloc(nsize);
	*sizep = nsize;

	dst = buffer;
	if (action == CRLF_GUESS) {
		/*
		 * If we guessed, we already know we rejected a file with
		 * lone CR, and we can strip a CR without looking at what
		 * follow it.
		 */
		do {
			unsigned char c = *src++;
			if (c != '\r')
				*dst++ = c;
		} while (--size);
	} else {
		do {
			unsigned char c = *src++;
			if (! (c == '\r' && (1 < size && *src == '\n')))
				*dst++ = c;
		} while (--size);
	}

	return buffer;
}

static char *crlf_to_worktree(const char *path, const char *src, unsigned long *sizep, int action)
{
	char *buffer, *dst;
	unsigned long size, nsize;
	struct text_stat stats;
	unsigned char last;

	if ((action == CRLF_BINARY) || (action == CRLF_INPUT) ||
	    auto_crlf <= 0)
		return NULL;

	size = *sizep;
	if (!size)
		return NULL;

	gather_stats(src, size, &stats);

	/* No LF? Nothing to convert, regardless. */
	if (!stats.lf)
		return NULL;

	/* Was it already in CRLF format? */
{'fraction_non_alphanumeric': 0.13985459352280238, 'fraction_numerical': 0.006345009914077991, 'mean_word_length': 2.942678478374153, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 27, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '848515', 'n_tokens_mistral': 3001, 'n_tokens_neox': 2619, 'n_words': 972}