|
Packit Service |
20376f |
/*
|
|
Packit Service |
20376f |
* Copyright (C) the libgit2 contributors. All rights reserved.
|
|
Packit Service |
20376f |
*
|
|
Packit Service |
20376f |
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
|
Packit Service |
20376f |
* a Linking Exception. For full terms see the included COPYING file.
|
|
Packit Service |
20376f |
*/
|
|
Packit Service |
20376f |
#include "buf_text.h"
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
int git_buf_text_puts_escaped(
|
|
Packit Service |
20376f |
git_buf *buf,
|
|
Packit Service |
20376f |
const char *string,
|
|
Packit Service |
20376f |
const char *esc_chars,
|
|
Packit Service |
20376f |
const char *esc_with)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
const char *scan;
|
|
Packit Service |
20376f |
size_t total = 0, esc_len = strlen(esc_with), count, alloclen;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (!string)
|
|
Packit Service |
20376f |
return 0;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
for (scan = string; *scan; ) {
|
|
Packit Service |
20376f |
/* count run of non-escaped characters */
|
|
Packit Service |
20376f |
count = strcspn(scan, esc_chars);
|
|
Packit Service |
20376f |
total += count;
|
|
Packit Service |
20376f |
scan += count;
|
|
Packit Service |
20376f |
/* count run of escaped characters */
|
|
Packit Service |
20376f |
count = strspn(scan, esc_chars);
|
|
Packit Service |
20376f |
total += count * (esc_len + 1);
|
|
Packit Service |
20376f |
scan += count;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
GITERR_CHECK_ALLOC_ADD(&alloclen, total, 1);
|
|
Packit Service |
20376f |
if (git_buf_grow_by(buf, alloclen) < 0)
|
|
Packit Service |
20376f |
return -1;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
for (scan = string; *scan; ) {
|
|
Packit Service |
20376f |
count = strcspn(scan, esc_chars);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
memmove(buf->ptr + buf->size, scan, count);
|
|
Packit Service |
20376f |
scan += count;
|
|
Packit Service |
20376f |
buf->size += count;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
for (count = strspn(scan, esc_chars); count > 0; --count) {
|
|
Packit Service |
20376f |
/* copy escape sequence */
|
|
Packit Service |
20376f |
memmove(buf->ptr + buf->size, esc_with, esc_len);
|
|
Packit Service |
20376f |
buf->size += esc_len;
|
|
Packit Service |
20376f |
/* copy character to be escaped */
|
|
Packit Service |
20376f |
buf->ptr[buf->size] = *scan;
|
|
Packit Service |
20376f |
buf->size++;
|
|
Packit Service |
20376f |
scan++;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
buf->ptr[buf->size] = '\0';
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
return 0;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
void git_buf_text_unescape(git_buf *buf)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
buf->size = git__unescape(buf->ptr);
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
int git_buf_text_crlf_to_lf(git_buf *tgt, const git_buf *src)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
const char *scan = src->ptr;
|
|
Packit Service |
20376f |
const char *scan_end = src->ptr + src->size;
|
|
Packit Service |
20376f |
const char *next = memchr(scan, '\r', src->size);
|
|
Packit Service |
20376f |
size_t new_size;
|
|
Packit Service |
20376f |
char *out;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
assert(tgt != src);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (!next)
|
|
Packit Service |
20376f |
return git_buf_set(tgt, src->ptr, src->size);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
/* reduce reallocs while in the loop */
|
|
Packit Service |
20376f |
GITERR_CHECK_ALLOC_ADD(&new_size, src->size, 1);
|
|
Packit Service |
20376f |
if (git_buf_grow(tgt, new_size) < 0)
|
|
Packit Service |
20376f |
return -1;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
out = tgt->ptr;
|
|
Packit Service |
20376f |
tgt->size = 0;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
/* Find the next \r and copy whole chunk up to there to tgt */
|
|
Packit Service |
20376f |
for (; next; scan = next + 1, next = memchr(scan, '\r', scan_end - scan)) {
|
|
Packit Service |
20376f |
if (next > scan) {
|
|
Packit Service |
20376f |
size_t copylen = (size_t)(next - scan);
|
|
Packit Service |
20376f |
memcpy(out, scan, copylen);
|
|
Packit Service |
20376f |
out += copylen;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
/* Do not drop \r unless it is followed by \n */
|
|
Packit Service |
20376f |
if (next + 1 == scan_end || next[1] != '\n')
|
|
Packit Service |
20376f |
*out++ = '\r';
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
/* Copy remaining input into dest */
|
|
Packit Service |
20376f |
if (scan < scan_end) {
|
|
Packit Service |
20376f |
size_t remaining = (size_t)(scan_end - scan);
|
|
Packit Service |
20376f |
memcpy(out, scan, remaining);
|
|
Packit Service |
20376f |
out += remaining;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
tgt->size = (size_t)(out - tgt->ptr);
|
|
Packit Service |
20376f |
tgt->ptr[tgt->size] = '\0';
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
return 0;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
int git_buf_text_lf_to_crlf(git_buf *tgt, const git_buf *src)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
const char *start = src->ptr;
|
|
Packit Service |
20376f |
const char *end = start + src->size;
|
|
Packit Service |
20376f |
const char *scan = start;
|
|
Packit Service |
20376f |
const char *next = memchr(scan, '\n', src->size);
|
|
Packit Service |
20376f |
size_t alloclen;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
assert(tgt != src);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (!next)
|
|
Packit Service |
20376f |
return git_buf_set(tgt, src->ptr, src->size);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
/* attempt to reduce reallocs while in the loop */
|
|
Packit Service |
20376f |
GITERR_CHECK_ALLOC_ADD(&alloclen, src->size, src->size >> 4);
|
|
Packit Service |
20376f |
GITERR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1);
|
|
Packit Service |
20376f |
if (git_buf_grow(tgt, alloclen) < 0)
|
|
Packit Service |
20376f |
return -1;
|
|
Packit Service |
20376f |
tgt->size = 0;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
for (; next; scan = next + 1, next = memchr(scan, '\n', end - scan)) {
|
|
Packit Service |
20376f |
size_t copylen = next - scan;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
/* if we find mixed line endings, carry on */
|
|
Packit Service |
20376f |
if (copylen && next[-1] == '\r')
|
|
Packit Service |
20376f |
copylen--;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
GITERR_CHECK_ALLOC_ADD(&alloclen, copylen, 3);
|
|
Packit Service |
20376f |
if (git_buf_grow_by(tgt, alloclen) < 0)
|
|
Packit Service |
20376f |
return -1;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (copylen) {
|
|
Packit Service |
20376f |
memcpy(tgt->ptr + tgt->size, scan, copylen);
|
|
Packit Service |
20376f |
tgt->size += copylen;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
tgt->ptr[tgt->size++] = '\r';
|
|
Packit Service |
20376f |
tgt->ptr[tgt->size++] = '\n';
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
tgt->ptr[tgt->size] = '\0';
|
|
Packit Service |
20376f |
return git_buf_put(tgt, scan, end - scan);
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
int git_buf_text_common_prefix(git_buf *buf, const git_strarray *strings)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
size_t i;
|
|
Packit Service |
20376f |
const char *str, *pfx;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
git_buf_clear(buf);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (!strings || !strings->count)
|
|
Packit Service |
20376f |
return 0;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
/* initialize common prefix to first string */
|
|
Packit Service |
20376f |
if (git_buf_sets(buf, strings->strings[0]) < 0)
|
|
Packit Service |
20376f |
return -1;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
/* go through the rest of the strings, truncating to shared prefix */
|
|
Packit Service |
20376f |
for (i = 1; i < strings->count; ++i) {
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
for (str = strings->strings[i], pfx = buf->ptr;
|
|
Packit Service |
20376f |
*str && *str == *pfx; str++, pfx++)
|
|
Packit Service |
20376f |
/* scanning */;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
git_buf_truncate(buf, pfx - buf->ptr);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (!buf->size)
|
|
Packit Service |
20376f |
break;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
return 0;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
bool git_buf_text_is_binary(const git_buf *buf)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
const char *scan = buf->ptr, *end = buf->ptr + buf->size;
|
|
Packit Service |
20376f |
git_bom_t bom;
|
|
Packit Service |
20376f |
int printable = 0, nonprintable = 0;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
scan += git_buf_text_detect_bom(&bom, buf, 0);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (bom > GIT_BOM_UTF8)
|
|
Packit Service |
20376f |
return 1;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
while (scan < end) {
|
|
Packit Service |
20376f |
unsigned char c = *scan++;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
/* Printable characters are those above SPACE (0x1F) excluding DEL,
|
|
Packit Service |
20376f |
* and including BS, ESC and FF.
|
|
Packit Service |
20376f |
*/
|
|
Packit Service |
20376f |
if ((c > 0x1F && c != 127) || c == '\b' || c == '\033' || c == '\014')
|
|
Packit Service |
20376f |
printable++;
|
|
Packit Service |
20376f |
else if (c == '\0')
|
|
Packit Service |
20376f |
return true;
|
|
Packit Service |
20376f |
else if (!git__isspace(c))
|
|
Packit Service |
20376f |
nonprintable++;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
return ((printable >> 7) < nonprintable);
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
bool git_buf_text_contains_nul(const git_buf *buf)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
return (memchr(buf->ptr, '\0', buf->size) != NULL);
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
int git_buf_text_detect_bom(git_bom_t *bom, const git_buf *buf, size_t offset)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
const char *ptr;
|
|
Packit Service |
20376f |
size_t len;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
*bom = GIT_BOM_NONE;
|
|
Packit Service |
20376f |
/* need at least 2 bytes after offset to look for any BOM */
|
|
Packit Service |
20376f |
if (buf->size < offset + 2)
|
|
Packit Service |
20376f |
return 0;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
ptr = buf->ptr + offset;
|
|
Packit Service |
20376f |
len = buf->size - offset;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
switch (*ptr++) {
|
|
Packit Service |
20376f |
case 0:
|
|
Packit Service |
20376f |
if (len >= 4 && ptr[0] == 0 && ptr[1] == '\xFE' && ptr[2] == '\xFF') {
|
|
Packit Service |
20376f |
*bom = GIT_BOM_UTF32_BE;
|
|
Packit Service |
20376f |
return 4;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
break;
|
|
Packit Service |
20376f |
case '\xEF':
|
|
Packit Service |
20376f |
if (len >= 3 && ptr[0] == '\xBB' && ptr[1] == '\xBF') {
|
|
Packit Service |
20376f |
*bom = GIT_BOM_UTF8;
|
|
Packit Service |
20376f |
return 3;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
break;
|
|
Packit Service |
20376f |
case '\xFE':
|
|
Packit Service |
20376f |
if (*ptr == '\xFF') {
|
|
Packit Service |
20376f |
*bom = GIT_BOM_UTF16_BE;
|
|
Packit Service |
20376f |
return 2;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
break;
|
|
Packit Service |
20376f |
case '\xFF':
|
|
Packit Service |
20376f |
if (*ptr != '\xFE')
|
|
Packit Service |
20376f |
break;
|
|
Packit Service |
20376f |
if (len >= 4 && ptr[1] == 0 && ptr[2] == 0) {
|
|
Packit Service |
20376f |
*bom = GIT_BOM_UTF32_LE;
|
|
Packit Service |
20376f |
return 4;
|
|
Packit Service |
20376f |
} else {
|
|
Packit Service |
20376f |
*bom = GIT_BOM_UTF16_LE;
|
|
Packit Service |
20376f |
return 2;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
break;
|
|
Packit Service |
20376f |
default:
|
|
Packit Service |
20376f |
break;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
return 0;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
bool git_buf_text_gather_stats(
|
|
Packit Service |
20376f |
git_buf_text_stats *stats, const git_buf *buf, bool skip_bom)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
const char *scan = buf->ptr, *end = buf->ptr + buf->size;
|
|
Packit Service |
20376f |
int skip;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
memset(stats, 0, sizeof(*stats));
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
/* BOM detection */
|
|
Packit Service |
20376f |
skip = git_buf_text_detect_bom(&stats->bom, buf, 0);
|
|
Packit Service |
20376f |
if (skip_bom)
|
|
Packit Service |
20376f |
scan += skip;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
/* Ignore EOF character */
|
|
Packit Service |
20376f |
if (buf->size > 0 && end[-1] == '\032')
|
|
Packit Service |
20376f |
end--;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
/* Counting loop */
|
|
Packit Service |
20376f |
while (scan < end) {
|
|
Packit Service |
20376f |
unsigned char c = *scan++;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (c > 0x1F && c != 0x7F)
|
|
Packit Service |
20376f |
stats->printable++;
|
|
Packit Service |
20376f |
else switch (c) {
|
|
Packit Service |
20376f |
case '\0':
|
|
Packit Service |
20376f |
stats->nul++;
|
|
Packit Service |
20376f |
stats->nonprintable++;
|
|
Packit Service |
20376f |
break;
|
|
Packit Service |
20376f |
case '\n':
|
|
Packit Service |
20376f |
stats->lf++;
|
|
Packit Service |
20376f |
break;
|
|
Packit Service |
20376f |
case '\r':
|
|
Packit Service |
20376f |
stats->cr++;
|
|
Packit Service |
20376f |
if (scan < end && *scan == '\n')
|
|
Packit Service |
20376f |
stats->crlf++;
|
|
Packit Service |
20376f |
break;
|
|
Packit Service |
20376f |
case '\t': case '\f': case '\v': case '\b': case 0x1b: /*ESC*/
|
|
Packit Service |
20376f |
stats->printable++;
|
|
Packit Service |
20376f |
break;
|
|
Packit Service |
20376f |
default:
|
|
Packit Service |
20376f |
stats->nonprintable++;
|
|
Packit Service |
20376f |
break;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
return (stats->nul > 0 ||
|
|
Packit Service |
20376f |
((stats->printable >> 7) < stats->nonprintable));
|
|
Packit Service |
20376f |
}
|