charset_macosxfs.c [plain text]
#include "includes.h"
#include <CoreFoundation/CFString.h>
#if HAVE_COREFOUNDATION_CFSTRINGENCODINGCONVERTER_H
# include <Corefoundation/CFStringEncodingConverter.h>
# include <Corefoundation/CFUnicodePrecomposition.h>
# define USE_INTERNAL_API 1
#elif HAVE_CFSTRINGENCODINGCONVERTER_H
# include <CFStringEncodingConverter.h>
# include <CFUnicodePrecomposition.h>
# define USE_INTERNAL_API 1
#endif
static inline void *resize_buffer (void *buffer, size_t *size, size_t newsize)
{
if (newsize > *size) {
*size = newsize + 128;
buffer = realloc(buffer, *size);
}
return buffer;
}
#ifdef WORDS_BIGENDIAN
static inline void swap_bytes (char * dst, const char * src, size_t len)
{
const char *srcend = src + len;
while (src < srcend) {
dst[0] = src[1];
dst[1] = src[0];
dst += 2;
src += 2;
}
}
static inline void swap_bytes_inplace (char * cp, size_t len)
{
char temp;
char *end = cp + len;
while (cp < end) {
temp = cp[1];
cp[1] = cp[0];
cp[0] = temp;
cp += 2;
}
}
#define le_to_native(dst,src,len) swap_bytes(dst,src,len)
#define native_to_le(cp,len) swap_bytes_inplace(cp,len)
#define set_ucbuffer_with_le(buffer,bufsize,data,size) \
set_ucbuffer_with_le_copy(buffer,bufsize,data,size,0)
#else
#define le_to_native(dst,src,len) memcpy(dst,src,len)
#define native_to_le(cp,len)
#define set_ucbuffer_with_le(buffer,bufsize,data,size) \
(((void)(bufsize)),(UniChar*)(data))
#endif
static inline UniChar *set_ucbuffer_with_le_copy (
UniChar *buffer, size_t *bufsize,
const void *data, size_t size, size_t reserve)
{
buffer = resize_buffer(buffer, bufsize, size+reserve);
le_to_native((char*)buffer,data,size);
return buffer;
}
#define debug_out(s) DEBUG(0,(s))
#ifdef DEBUG_STRINGS
static void hexdump( const char * label, const char * s, size_t len )
{
size_t restlen = len;
debug_out("<<<<<<<\n");
debug_out(label);
debug_out("\n");
while (restlen > 0) {
char line[100];
size_t i, j;
char * d = line;
#undef sprintf
d += sprintf(d, "%04X ", (unsigned)(len-restlen));
*d++ = ' ';
for( i = 0; i<restlen && i<8; ++i ) {
d += sprintf(d, "%02X ", ((unsigned)s[i]) & 0xFF);
}
for( j = i; j<8; ++j ) {
d += sprintf(d, " ");
}
*d++ = ' ';
for( i = 8; i<restlen && i<16; ++i ) {
d += sprintf(d, "%02X ", ((unsigned)s[i]) & 0xFF);
}
for( j = i; j<16; ++j ) {
d += sprintf(d, " ");
}
*d++ = ' ';
for( i = 0; i<restlen && i<16; ++i ) {
if(s[i] < ' ' || s[i] >= 0x7F || !isprint(s[i]))
*d++ = '.';
else
*d++ = s[i];
}
*d++ = '\n';
*d = 0;
restlen -= i;
s += i;
debug_out(line);
}
debug_out(">>>>>>>\n");
}
#else
#define hexdump(label,s,len)
#endif
#if !USE_INTERNAL_API
static size_t macosxfs_encoding_pull(
void *cd,
char **inbuf, size_t *inbytesleft,
char **outbuf, size_t *outbytesleft)
{
static const int script_code = kCFStringEncodingUTF8;
static CFMutableStringRef cfstring = NULL;
size_t outsize;
CFRange range;
(void) cd;
if (0 == *inbytesleft) {
return 0;
}
if (NULL == cfstring) {
cfstring = CFStringCreateMutable(kCFAllocatorDefault,0);
}
if (0 == (*inbuf)[*inbytesleft-1]) {
CFStringAppendCString(cfstring, *inbuf, script_code);
} else if (*inbytesleft <= 255) {
Str255 buffer;
buffer[0] = *inbytesleft;
memcpy(buffer+1, *inbuf, buffer[0]);
CFStringAppendPascalString(cfstring, buffer, script_code);
} else {
static char *buffer = NULL;
static size_t buflen = 0;
buffer = resize_buffer(buffer, &buflen, *inbytesleft+1);
memcpy(buffer, *inbuf, *inbytesleft);
buffer[*inbytesleft] = 0;
CFStringAppendCString(cfstring, *inbuf, script_code);
}
CFStringNormalize(cfstring, kCFStringNormalizationFormC);
outsize = CFStringGetLength(cfstring);
range = CFRangeMake(0,outsize);
if (outsize == 0) {
if(2 != *inbytesleft && 10 != *inbytesleft) {
debug_out("String conversion: "
"An unknown error occurred\n");
hexdump("UTF8->UTF16LE (old) input",
*inbuf, *inbytesleft);
}
errno = EILSEQ;
return -1;
}
if (outsize*2 > *outbytesleft) {
CFStringDelete(cfstring, range);
debug_out("String conversion: "
"Output buffer too small\n");
hexdump("UTF8->UTF16LE (old) input",
*inbuf, *inbytesleft);
errno = E2BIG;
return -1;
}
CFStringGetCharacters(cfstring, range, (UniChar*)*outbuf);
CFStringDelete(cfstring, range);
native_to_le(*outbuf, outsize*2);
if (0 == (*inbuf)[*inbytesleft-1] &&
(0 != (*outbuf)[outsize*2-1] || 0 != (*outbuf)[outsize*2-2])) {
if ((outsize*2+2) > *outbytesleft) {
debug_out("String conversion: "
"Output buffer too small\n");
hexdump("UTF8->UTF16LE (old) input",
*inbuf, *inbytesleft);
errno = E2BIG;
return -1;
}
(*outbuf)[outsize*2] = (*outbuf)[outsize*2+1] = 0;
outsize += 2;
}
*inbuf += *inbytesleft;
*inbytesleft = 0;
*outbuf += outsize*2;
*outbytesleft -= outsize*2;
return 0;
}
static size_t macosxfs_encoding_push(
void *cd,
char **inbuf, size_t *inbytesleft,
char **outbuf, size_t *outbytesleft)
{
static const int script_code = kCFStringEncodingUTF8;
static CFMutableStringRef cfstring = NULL;
static UniChar *buffer = NULL;
static size_t buflen = 0;
CFIndex outsize, cfsize, charsconverted;
(void) cd;
if (0 == *inbytesleft) {
return 0;
}
buffer = set_ucbuffer_with_le_copy(
buffer, &buflen, *inbuf, *inbytesleft, 3 * *inbytesleft);
if (NULL == cfstring) {
cfstring = CFStringCreateMutableWithExternalCharactersNoCopy(
kCFAllocatorDefault,
buffer, *inbytesleft/2, buflen/2,
kCFAllocatorNull);
} else {
CFStringSetExternalCharactersNoCopy(
cfstring,
buffer, *inbytesleft/2, buflen/2);
}
CFStringNormalize(cfstring, kCFStringNormalizationFormD);
cfsize = CFStringGetLength(cfstring);
charsconverted = CFStringGetBytes(
cfstring, CFRangeMake(0,cfsize),
script_code, 0, False,
*outbuf, *outbytesleft, &outsize);
if (0 == charsconverted) {
debug_out("String conversion: "
"Buffer too small or not convertable\n");
hexdump("UTF16LE->UTF8 (old) input",
*inbuf, *inbytesleft);
errno = EILSEQ;
return -1;
}
if (0 == (*inbuf)[*inbytesleft-1] && 0 == (*inbuf)[*inbytesleft-2] &&
(0 != (*outbuf)[outsize-1])) {
if (((size_t)outsize+1) > *outbytesleft) {
debug_out("String conversion: "
"Output buffer too small\n");
hexdump("UTF16LE->UTF8 (old) input",
*inbuf, *inbytesleft);
errno = E2BIG;
return -1;
}
(*outbuf)[outsize] = 0;
++outsize;
}
*inbuf += *inbytesleft;
*inbytesleft = 0;
*outbuf += outsize;
*outbytesleft -= outsize;
return 0;
}
#else
static size_t macosxfs_encoding_pull(
void *cd,
char **inbuf, size_t *inbytesleft,
char **outbuf, size_t *outbytesleft)
{
static const int script_code = kCFStringEncodingUTF8;
UInt32 srcCharsUsed = 0;
UInt32 dstCharsUsed = 0;
UInt32 result;
uint32_t dstDecomposedUsed = 0;
uint32_t dstPrecomposedUsed = 0;
(void) cd;
if (0 == *inbytesleft) {
return 0;
}
result = CFStringEncodingBytesToUnicode(
script_code, kCFStringEncodingComposeCombinings,
*inbuf, *inbytesleft, &srcCharsUsed,
(UniChar*)*outbuf, *outbytesleft, &dstCharsUsed);
switch(result) {
case kCFStringEncodingConversionSuccess:
if (*inbytesleft == srcCharsUsed)
break;
else
;
case kCFStringEncodingInsufficientOutputBufferLength:
debug_out("String conversion: "
"Output buffer too small\n");
hexdump("UTF8->UTF16LE (new) input",
*inbuf, *inbytesleft);
errno = E2BIG;
return -1;
case kCFStringEncodingInvalidInputStream:
if(2 != *inbytesleft && 10 != *inbytesleft) {
debug_out("String conversion: "
"Invalid input sequence\n");
hexdump("UTF8->UTF16LE (new) input",
*inbuf, *inbytesleft);
}
errno = EILSEQ;
return -1;
case kCFStringEncodingConverterUnavailable:
debug_out("String conversion: "
"Unknown encoding\n");
hexdump("UTF8->UTF16LE (new) input",
*inbuf, *inbytesleft);
errno = EINVAL;
return -1;
}
CFUniCharPrecompose(
(const UTF16Char *)*outbuf, dstCharsUsed, &dstDecomposedUsed,
(UTF16Char *)*outbuf, dstCharsUsed, &dstPrecomposedUsed);
native_to_le(*outbuf, dstPrecomposedUsed*2);
*inbuf += srcCharsUsed;
*inbytesleft -= srcCharsUsed;
*outbuf += dstPrecomposedUsed*2;
*outbytesleft -= dstPrecomposedUsed*2;
return 0;
}
static size_t macosxfs_encoding_push(
void *cd,
char **inbuf, size_t *inbytesleft,
char **outbuf, size_t *outbytesleft)
{
static const int script_code = kCFStringEncodingUTF8;
static UniChar *buffer = NULL;
static size_t buflen = 0;
UInt32 srcCharsUsed=0, dstCharsUsed=0, result;
(void) cd;
if (0 == *inbytesleft) {
return 0;
}
buffer = set_ucbuffer_with_le(
buffer, &buflen, *inbuf, *inbytesleft);
result = CFStringEncodingUnicodeToBytes(
script_code, kCFStringEncodingUseHFSPlusCanonical,
buffer, *inbytesleft/2, &srcCharsUsed,
*outbuf, *outbytesleft, &dstCharsUsed);
switch(result) {
case kCFStringEncodingConversionSuccess:
if (*inbytesleft/2 == srcCharsUsed)
break;
else
;
case kCFStringEncodingInsufficientOutputBufferLength:
debug_out("String conversion: "
"Output buffer too small\n");
hexdump("UTF16LE->UTF8 (new) input",
*inbuf, *inbytesleft);
errno = E2BIG;
return -1;
case kCFStringEncodingInvalidInputStream:
if(10 != *inbytesleft) {
debug_out("String conversion: "
"Invalid input sequence\n");
hexdump("UTF16LE->UTF8 (new) input",
*inbuf, *inbytesleft);
}
errno = EILSEQ;
return -1;
case kCFStringEncodingConverterUnavailable:
debug_out("String conversion: "
"Unknown encoding\n");
hexdump("UTF16LE->UTF8 (new) input",
*inbuf, *inbytesleft);
errno = EINVAL;
return -1;
}
*inbuf += srcCharsUsed*2;
*inbytesleft -= srcCharsUsed*2;
*outbuf += dstCharsUsed;
*outbytesleft -= dstCharsUsed;
return 0;
}
#endif
static struct charset_functions macosxfs_encoding_functions = {
"MACOSXFS", macosxfs_encoding_pull, macosxfs_encoding_push
};
NTSTATUS init_module(void)
{
return smb_register_charset(&macosxfs_encoding_functions);
}