2001-07-04 07:15:53 +00:00
/*
2002-01-30 06:08:46 +00:00
Unix SMB / CIFS implementation .
2001-07-04 07:15:53 +00:00
minimal iconv implementation
Copyright ( C ) Andrew Tridgell 2001
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 2 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
# include "includes.h"
2001-07-22 07:38:32 +00:00
static size_t ascii_pull ( void * , char * * , size_t * , char * * , size_t * ) ;
static size_t ascii_push ( void * , char * * , size_t * , char * * , size_t * ) ;
static size_t utf8_pull ( void * , char * * , size_t * , char * * , size_t * ) ;
static size_t utf8_push ( void * , char * * , size_t * , char * * , size_t * ) ;
static size_t weird_pull ( void * , char * * , size_t * , char * * , size_t * ) ;
static size_t weird_push ( void * , char * * , size_t * , char * * , size_t * ) ;
static size_t ucs2hex_pull ( void * , char * * , size_t * , char * * , size_t * ) ;
static size_t ucs2hex_push ( void * , char * * , size_t * , char * * , size_t * ) ;
static size_t iconv_copy ( void * , char * * , size_t * , char * * , size_t * ) ;
2001-07-04 07:15:53 +00:00
/*
for each charset we have a function that pulls from that charset to
a ucs2 buffer , and a function that pushes to a ucs2 buffer
*/
static struct {
Patch from Michael Steffens. In his own words :
-------------------------------------------------------------------------
I think there are basically two problem:
1. Windows clients do not always send ACEs for SMB_ACL_USER_OBJ, SMB_ACL_GROUP_OBJ,
and SMB_ACL_OTHER.
The function ensure_canon_entry_valid() is prepared for that, but tries
to "guess" values from group or other permissions, respectively, otherwise
falling back to minimum r-- for the owner. Even if the owner had full
permissions before setting ACL. This is the problem with W2k clients.
2. Function set_nt_acl() always chowns *before* attempting to set POSIX ACLs.
This is ok in a take-ownership situation, but must fail if the file is
to be given away. This is the problem with XP clients, trying to transfer
ownership of the original file to the temp file.
The problem with NT4 clients (no ACEs are transferred to the temp file, thus
are lost after moving the temp file to the original name) is a client problem.
It simply doesn't attempt to.
I have played around with that using posic_acls.c from 3.0 merged into 2.2.
As a result I can now present two patches, one for each branch. They
basically modify:
1. Interpret missing SMB_ACL_USER_OBJ, SMB_ACL_GROUP_OBJ, or SMB_ACL_OTHER
as "preserve current value" instead of attempting to build one ourself.
The original code is still in, but only as fallback in case current values
can't be retrieved.
2. Rearrange set_nt_acl() such that chown is only done before setting
ACLs if there is either no change of owning user, or change of owning
user is towards the current user. Otherwise chown is done after setting
ACLs.
It now seems to produce reasonable results. (Well, as far as it can. If
NT4 doesn't even try to transfer ACEs, only deliberate use of named default
ACEs and/or "force group" or the crystal ball can help :)
-------------------------------------------------------------------------
Jeremy.
(This used to be commit 1d3b8c528bebfa1971d1affe454a03453335786e)
2003-03-07 19:37:31 +00:00
const char * name ;
2001-07-22 07:38:32 +00:00
size_t ( * pull ) ( void * , char * * inbuf , size_t * inbytesleft ,
2001-07-04 07:15:53 +00:00
char * * outbuf , size_t * outbytesleft ) ;
2001-07-22 07:38:32 +00:00
size_t ( * push ) ( void * , char * * inbuf , size_t * inbytesleft ,
2001-07-04 07:15:53 +00:00
char * * outbuf , size_t * outbytesleft ) ;
} charsets [ ] = {
2001-07-22 00:27:30 +00:00
{ " UCS-2LE " , iconv_copy , iconv_copy } ,
2001-07-04 15:08:30 +00:00
{ " UTF8 " , utf8_pull , utf8_push } ,
2001-07-04 07:15:53 +00:00
{ " ASCII " , ascii_pull , ascii_push } ,
{ " WEIRD " , weird_pull , weird_push } ,
2001-07-22 07:38:32 +00:00
{ " UCS2-HEX " , ucs2hex_pull , ucs2hex_push } ,
2001-07-04 07:15:53 +00:00
{ NULL , NULL , NULL }
} ;
2001-07-22 07:38:32 +00:00
/* if there was an error then reset the internal state,
this ensures that we don ' t have a shift state remaining for
character sets like SJIS */
static size_t sys_iconv ( void * cd ,
char * * inbuf , size_t * inbytesleft ,
char * * outbuf , size_t * outbytesleft )
{
# ifdef HAVE_NATIVE_ICONV
size_t ret = iconv ( ( iconv_t ) cd ,
inbuf , inbytesleft ,
outbuf , outbytesleft ) ;
if ( ret = = ( size_t ) - 1 ) iconv ( cd , NULL , NULL , NULL , NULL ) ;
return ret ;
# else
errno = EINVAL ;
return - 1 ;
# endif
}
2003-02-27 05:57:21 +00:00
/**
* This is a simple portable iconv ( ) implementaion .
*
* It only knows about a very small number of character sets - just
* enough that Samba works on systems that don ' t have iconv .
* */
2001-07-04 07:15:53 +00:00
size_t smb_iconv ( smb_iconv_t cd ,
2001-09-29 11:51:40 +00:00
const char * * inbuf , size_t * inbytesleft ,
2001-07-04 07:15:53 +00:00
char * * outbuf , size_t * outbytesleft )
{
char cvtbuf [ 2048 ] ;
char * bufp = cvtbuf ;
size_t bufsize ;
2001-07-22 07:38:32 +00:00
/* in many cases we can go direct */
2001-07-04 07:15:53 +00:00
if ( cd - > direct ) {
2001-07-22 07:38:32 +00:00
return cd - > direct ( cd - > cd_direct ,
2001-10-23 19:10:30 +00:00
( char * * ) inbuf , inbytesleft , outbuf , outbytesleft ) ;
2001-07-04 07:15:53 +00:00
}
2001-07-22 07:38:32 +00:00
2001-07-04 07:15:53 +00:00
/* otherwise we have to do it chunks at a time */
while ( * inbytesleft > 0 ) {
bufp = cvtbuf ;
bufsize = sizeof ( cvtbuf ) ;
2001-07-22 07:38:32 +00:00
if ( cd - > pull ( cd - > cd_pull ,
2001-10-23 19:10:30 +00:00
( char * * ) inbuf , inbytesleft , & bufp , & bufsize ) = = - 1
2001-07-22 07:38:32 +00:00
& & errno ! = E2BIG ) return - 1 ;
2001-07-04 07:15:53 +00:00
bufp = cvtbuf ;
bufsize = sizeof ( cvtbuf ) - bufsize ;
2001-07-22 07:38:32 +00:00
if ( cd - > push ( cd - > cd_push ,
& bufp , & bufsize ,
outbuf , outbytesleft ) = = - 1 ) return - 1 ;
2001-07-04 07:15:53 +00:00
}
return 0 ;
}
/*
simple iconv_open ( ) wrapper
*/
smb_iconv_t smb_iconv_open ( const char * tocode , const char * fromcode )
{
smb_iconv_t ret ;
int from , to ;
2001-07-22 07:38:32 +00:00
ret = ( smb_iconv_t ) malloc ( sizeof ( * ret ) ) ;
if ( ! ret ) {
errno = ENOMEM ;
return ( smb_iconv_t ) - 1 ;
}
memset ( ret , 0 , sizeof ( * ret ) ) ;
2001-12-20 06:18:52 +00:00
ret - > from_name = strdup ( fromcode ) ;
ret - > to_name = strdup ( tocode ) ;
2001-07-22 07:38:32 +00:00
/* check for the simplest null conversion */
if ( strcmp ( fromcode , tocode ) = = 0 ) {
ret - > direct = iconv_copy ;
return ret ;
}
2001-07-04 07:15:53 +00:00
for ( from = 0 ; charsets [ from ] . name ; from + + ) {
if ( strcasecmp ( charsets [ from ] . name , fromcode ) = = 0 ) break ;
}
for ( to = 0 ; charsets [ to ] . name ; to + + ) {
if ( strcasecmp ( charsets [ to ] . name , tocode ) = = 0 ) break ;
}
# ifdef HAVE_NATIVE_ICONV
2001-07-22 07:38:32 +00:00
if ( ! charsets [ from ] . name ) {
ret - > pull = sys_iconv ;
ret - > cd_pull = iconv_open ( " UCS-2LE " , fromcode ) ;
2001-09-07 14:14:57 +00:00
if ( ret - > cd_pull = = ( iconv_t ) - 1 ) goto failed ;
2001-07-04 07:15:53 +00:00
}
2001-07-22 07:38:32 +00:00
if ( ! charsets [ to ] . name ) {
ret - > push = sys_iconv ;
ret - > cd_push = iconv_open ( tocode , " UCS-2LE " ) ;
2001-09-07 14:14:57 +00:00
if ( ret - > cd_push = = ( iconv_t ) - 1 ) goto failed ;
2001-07-04 07:15:53 +00:00
}
2001-07-22 07:38:32 +00:00
# else
if ( ! charsets [ from ] . name | | ! charsets [ to ] . name ) {
goto failed ;
2001-07-04 07:15:53 +00:00
}
# endif
2001-07-22 07:38:32 +00:00
/* check for conversion to/from ucs2 */
if ( from = = 0 & & charsets [ to ] . name ) {
ret - > direct = charsets [ to ] . push ;
return ret ;
}
if ( to = = 0 & & charsets [ from ] . name ) {
ret - > direct = charsets [ from ] . pull ;
2001-07-04 07:15:53 +00:00
return ret ;
}
2001-07-22 07:38:32 +00:00
# ifdef HAVE_NATIVE_ICONV
2001-07-04 07:15:53 +00:00
if ( from = = 0 ) {
2001-07-22 07:38:32 +00:00
ret - > direct = sys_iconv ;
ret - > cd_direct = ret - > cd_push ;
ret - > cd_push = NULL ;
2001-07-04 07:15:53 +00:00
return ret ;
}
if ( to = = 0 ) {
2001-07-22 07:38:32 +00:00
ret - > direct = sys_iconv ;
ret - > cd_direct = ret - > cd_pull ;
ret - > cd_pull = NULL ;
2001-07-04 07:15:53 +00:00
return ret ;
}
2001-07-22 07:38:32 +00:00
# endif
2001-07-04 07:15:53 +00:00
/* the general case has to go via a buffer */
2001-07-22 07:38:32 +00:00
if ( ! ret - > pull ) ret - > pull = charsets [ from ] . pull ;
if ( ! ret - > push ) ret - > push = charsets [ to ] . push ;
2001-07-04 07:15:53 +00:00
return ret ;
2001-07-22 07:38:32 +00:00
failed :
2001-09-17 02:19:44 +00:00
SAFE_FREE ( ret ) ;
2001-07-22 07:38:32 +00:00
errno = EINVAL ;
return ( smb_iconv_t ) - 1 ;
2001-07-04 07:15:53 +00:00
}
/*
simple iconv_close ( ) wrapper
*/
int smb_iconv_close ( smb_iconv_t cd )
{
# ifdef HAVE_NATIVE_ICONV
2001-07-22 07:38:32 +00:00
if ( cd - > cd_direct ) iconv_close ( ( iconv_t ) cd - > cd_direct ) ;
if ( cd - > cd_pull ) iconv_close ( ( iconv_t ) cd - > cd_pull ) ;
if ( cd - > cd_push ) iconv_close ( ( iconv_t ) cd - > cd_push ) ;
2001-07-04 07:15:53 +00:00
# endif
2001-07-22 07:38:32 +00:00
2001-12-20 06:18:52 +00:00
SAFE_FREE ( cd - > from_name ) ;
SAFE_FREE ( cd - > to_name ) ;
2001-07-04 07:15:53 +00:00
memset ( cd , 0 , sizeof ( * cd ) ) ;
2001-09-17 02:19:44 +00:00
SAFE_FREE ( cd ) ;
2001-07-04 07:15:53 +00:00
return 0 ;
}
/**********************************************************************
the following functions implement the builtin character sets in Samba
and also the " test " character sets that are designed to test
multi - byte character set support for english users
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2001-07-22 07:38:32 +00:00
static size_t ascii_pull ( void * cd , char * * inbuf , size_t * inbytesleft ,
2001-07-04 07:15:53 +00:00
char * * outbuf , size_t * outbytesleft )
{
while ( * inbytesleft > = 1 & & * outbytesleft > = 2 ) {
( * outbuf ) [ 0 ] = ( * inbuf ) [ 0 ] ;
( * outbuf ) [ 1 ] = 0 ;
( * inbytesleft ) - = 1 ;
( * outbytesleft ) - = 2 ;
( * inbuf ) + = 1 ;
( * outbuf ) + = 2 ;
}
if ( * inbytesleft > 0 ) {
errno = E2BIG ;
return - 1 ;
}
return 0 ;
}
2001-07-22 07:38:32 +00:00
static size_t ascii_push ( void * cd , char * * inbuf , size_t * inbytesleft ,
2001-07-04 07:15:53 +00:00
char * * outbuf , size_t * outbytesleft )
{
int ir_count = 0 ;
while ( * inbytesleft > = 2 & & * outbytesleft > = 1 ) {
2001-12-20 06:18:52 +00:00
( * outbuf ) [ 0 ] = ( * inbuf ) [ 0 ] & 0x7F ;
2001-07-04 07:15:53 +00:00
if ( ( * inbuf ) [ 1 ] ) ir_count + + ;
( * inbytesleft ) - = 2 ;
( * outbytesleft ) - = 1 ;
( * inbuf ) + = 2 ;
( * outbuf ) + = 1 ;
}
if ( * inbytesleft = = 1 ) {
errno = EINVAL ;
return - 1 ;
}
if ( * inbytesleft > 1 ) {
errno = E2BIG ;
return - 1 ;
}
return ir_count ;
}
2001-07-22 07:38:32 +00:00
static size_t ucs2hex_pull ( void * cd , char * * inbuf , size_t * inbytesleft ,
char * * outbuf , size_t * outbytesleft )
{
while ( * inbytesleft > = 1 & & * outbytesleft > = 2 ) {
unsigned v ;
if ( ( * inbuf ) [ 0 ] ! = ' @ ' ) {
/* seven bit ascii case */
( * outbuf ) [ 0 ] = ( * inbuf ) [ 0 ] ;
( * outbuf ) [ 1 ] = 0 ;
( * inbytesleft ) - = 1 ;
( * outbytesleft ) - = 2 ;
( * inbuf ) + = 1 ;
( * outbuf ) + = 2 ;
continue ;
}
/* it's a hex character */
if ( * inbytesleft < 5 ) {
errno = EINVAL ;
return - 1 ;
}
if ( sscanf ( & ( * inbuf ) [ 1 ] , " %04x " , & v ) ! = 1 ) {
errno = EILSEQ ;
return - 1 ;
}
( * outbuf ) [ 0 ] = v & 0xff ;
( * outbuf ) [ 1 ] = v > > 8 ;
( * inbytesleft ) - = 5 ;
( * outbytesleft ) - = 2 ;
( * inbuf ) + = 5 ;
( * outbuf ) + = 2 ;
}
if ( * inbytesleft > 0 ) {
errno = E2BIG ;
return - 1 ;
}
return 0 ;
}
static size_t ucs2hex_push ( void * cd , char * * inbuf , size_t * inbytesleft ,
char * * outbuf , size_t * outbytesleft )
{
while ( * inbytesleft > = 2 & & * outbytesleft > = 1 ) {
char buf [ 6 ] ;
if ( ( * inbuf ) [ 1 ] = = 0 & &
( ( * inbuf ) [ 0 ] & 0x80 ) = = 0 & &
( * inbuf ) [ 0 ] ! = ' @ ' ) {
( * outbuf ) [ 0 ] = ( * inbuf ) [ 0 ] ;
( * inbytesleft ) - = 2 ;
( * outbytesleft ) - = 1 ;
( * inbuf ) + = 2 ;
( * outbuf ) + = 1 ;
continue ;
}
if ( * outbytesleft < 5 ) {
errno = E2BIG ;
return - 1 ;
}
snprintf ( buf , 6 , " @%04x " , SVAL ( * inbuf , 0 ) ) ;
memcpy ( * outbuf , buf , 5 ) ;
( * inbytesleft ) - = 2 ;
( * outbytesleft ) - = 5 ;
( * inbuf ) + = 2 ;
( * outbuf ) + = 5 ;
}
if ( * inbytesleft = = 1 ) {
errno = EINVAL ;
return - 1 ;
}
if ( * inbytesleft > 1 ) {
errno = E2BIG ;
return - 1 ;
}
return 0 ;
}
2001-07-04 07:15:53 +00:00
/* the "weird" character set is very useful for testing multi-byte
support and finding bugs . Don ' t use on a production system !
*/
static struct {
Patch from Michael Steffens. In his own words :
-------------------------------------------------------------------------
I think there are basically two problem:
1. Windows clients do not always send ACEs for SMB_ACL_USER_OBJ, SMB_ACL_GROUP_OBJ,
and SMB_ACL_OTHER.
The function ensure_canon_entry_valid() is prepared for that, but tries
to "guess" values from group or other permissions, respectively, otherwise
falling back to minimum r-- for the owner. Even if the owner had full
permissions before setting ACL. This is the problem with W2k clients.
2. Function set_nt_acl() always chowns *before* attempting to set POSIX ACLs.
This is ok in a take-ownership situation, but must fail if the file is
to be given away. This is the problem with XP clients, trying to transfer
ownership of the original file to the temp file.
The problem with NT4 clients (no ACEs are transferred to the temp file, thus
are lost after moving the temp file to the original name) is a client problem.
It simply doesn't attempt to.
I have played around with that using posic_acls.c from 3.0 merged into 2.2.
As a result I can now present two patches, one for each branch. They
basically modify:
1. Interpret missing SMB_ACL_USER_OBJ, SMB_ACL_GROUP_OBJ, or SMB_ACL_OTHER
as "preserve current value" instead of attempting to build one ourself.
The original code is still in, but only as fallback in case current values
can't be retrieved.
2. Rearrange set_nt_acl() such that chown is only done before setting
ACLs if there is either no change of owning user, or change of owning
user is towards the current user. Otherwise chown is done after setting
ACLs.
It now seems to produce reasonable results. (Well, as far as it can. If
NT4 doesn't even try to transfer ACEs, only deliberate use of named default
ACEs and/or "force group" or the crystal ball can help :)
-------------------------------------------------------------------------
Jeremy.
(This used to be commit 1d3b8c528bebfa1971d1affe454a03453335786e)
2003-03-07 19:37:31 +00:00
const char from ;
const char * to ;
2001-07-04 07:15:53 +00:00
int len ;
} weird_table [ ] = {
{ ' q ' , " ^q^ " , 3 } ,
{ ' Q ' , " ^Q^ " , 3 } ,
{ 0 , NULL }
} ;
2001-07-22 07:38:32 +00:00
static size_t weird_pull ( void * cd , char * * inbuf , size_t * inbytesleft ,
2001-07-04 07:15:53 +00:00
char * * outbuf , size_t * outbytesleft )
{
while ( * inbytesleft > = 1 & & * outbytesleft > = 2 ) {
int i ;
2001-07-04 12:16:24 +00:00
int done = 0 ;
2001-07-04 07:15:53 +00:00
for ( i = 0 ; weird_table [ i ] . from ; i + + ) {
if ( strncmp ( ( * inbuf ) ,
weird_table [ i ] . to ,
weird_table [ i ] . len ) = = 0 ) {
if ( * inbytesleft < weird_table [ i ] . len ) {
DEBUG ( 0 , ( " ERROR: truncated weird string \n " ) ) ;
2001-07-04 14:41:06 +00:00
/* smb_panic("weird_pull"); */
2001-07-04 07:15:53 +00:00
} else {
( * outbuf ) [ 0 ] = weird_table [ i ] . from ;
( * outbuf ) [ 1 ] = 0 ;
( * inbytesleft ) - = weird_table [ i ] . len ;
( * outbytesleft ) - = 2 ;
( * inbuf ) + = weird_table [ i ] . len ;
( * outbuf ) + = 2 ;
2001-07-04 12:16:24 +00:00
done = 1 ;
break ;
2001-07-04 07:15:53 +00:00
}
}
}
2001-07-04 12:16:24 +00:00
if ( done ) continue ;
2001-07-04 07:15:53 +00:00
( * outbuf ) [ 0 ] = ( * inbuf ) [ 0 ] ;
( * outbuf ) [ 1 ] = 0 ;
( * inbytesleft ) - = 1 ;
( * outbytesleft ) - = 2 ;
( * inbuf ) + = 1 ;
( * outbuf ) + = 2 ;
}
if ( * inbytesleft > 0 ) {
errno = E2BIG ;
return - 1 ;
}
return 0 ;
}
2001-07-22 07:38:32 +00:00
static size_t weird_push ( void * cd , char * * inbuf , size_t * inbytesleft ,
2001-07-04 07:15:53 +00:00
char * * outbuf , size_t * outbytesleft )
{
int ir_count = 0 ;
while ( * inbytesleft > = 2 & & * outbytesleft > = 1 ) {
int i ;
2001-07-04 12:16:24 +00:00
int done = 0 ;
2001-07-04 07:15:53 +00:00
for ( i = 0 ; weird_table [ i ] . from ; i + + ) {
if ( ( * inbuf ) [ 0 ] = = weird_table [ i ] . from & &
( * inbuf ) [ 1 ] = = 0 ) {
if ( * outbytesleft < weird_table [ i ] . len ) {
DEBUG ( 0 , ( " No room for weird character \n " ) ) ;
2001-07-04 14:41:06 +00:00
/* smb_panic("weird_push"); */
2001-07-04 07:15:53 +00:00
} else {
memcpy ( * outbuf , weird_table [ i ] . to ,
weird_table [ i ] . len ) ;
( * inbytesleft ) - = 2 ;
( * outbytesleft ) - = weird_table [ i ] . len ;
( * inbuf ) + = 2 ;
( * outbuf ) + = weird_table [ i ] . len ;
2001-07-04 12:16:24 +00:00
done = 1 ;
break ;
2001-07-04 07:15:53 +00:00
}
}
}
2001-07-04 12:16:24 +00:00
if ( done ) continue ;
2001-07-04 07:15:53 +00:00
( * outbuf ) [ 0 ] = ( * inbuf ) [ 0 ] ;
if ( ( * inbuf ) [ 1 ] ) ir_count + + ;
( * inbytesleft ) - = 2 ;
( * outbytesleft ) - = 1 ;
( * inbuf ) + = 2 ;
( * outbuf ) + = 1 ;
}
if ( * inbytesleft = = 1 ) {
errno = EINVAL ;
return - 1 ;
}
if ( * inbytesleft > 1 ) {
errno = E2BIG ;
return - 1 ;
}
return ir_count ;
}
2001-07-22 07:38:32 +00:00
static size_t iconv_copy ( void * cd , char * * inbuf , size_t * inbytesleft ,
2001-07-04 07:15:53 +00:00
char * * outbuf , size_t * outbytesleft )
{
int n ;
n = MIN ( * inbytesleft , * outbytesleft ) ;
memmove ( * outbuf , * inbuf , n ) ;
( * inbytesleft ) - = n ;
( * outbytesleft ) - = n ;
( * inbuf ) + = n ;
( * outbuf ) + = n ;
if ( * inbytesleft > 0 ) {
errno = E2BIG ;
return - 1 ;
}
return 0 ;
}
2001-07-04 15:08:30 +00:00
2001-07-22 07:38:32 +00:00
static size_t utf8_pull ( void * cd , char * * inbuf , size_t * inbytesleft ,
2001-07-04 15:08:30 +00:00
char * * outbuf , size_t * outbytesleft )
{
while ( * inbytesleft > = 1 & & * outbytesleft > = 2 ) {
unsigned char * c = ( unsigned char * ) * inbuf ;
unsigned char * uc = ( unsigned char * ) * outbuf ;
int len = 1 ;
2001-07-05 00:57:42 +00:00
if ( ( c [ 0 ] & 0x80 ) = = 0 ) {
uc [ 0 ] = c [ 0 ] ;
uc [ 1 ] = 0 ;
} else if ( ( c [ 0 ] & 0xf0 ) = = 0xe0 ) {
2001-07-04 15:08:30 +00:00
if ( * inbytesleft < 3 ) {
DEBUG ( 0 , ( " short utf8 char \n " ) ) ;
goto badseq ;
}
uc [ 1 ] = ( ( c [ 0 ] & 0xF ) < < 4 ) | ( ( c [ 1 ] > > 2 ) & 0xF ) ;
uc [ 0 ] = ( c [ 1 ] < < 6 ) | ( c [ 2 ] & 0x3f ) ;
len = 3 ;
} else if ( ( c [ 0 ] & 0xe0 ) = = 0xc0 ) {
if ( * inbytesleft < 2 ) {
DEBUG ( 0 , ( " short utf8 char \n " ) ) ;
goto badseq ;
}
uc [ 1 ] = ( c [ 0 ] > > 2 ) & 0x7 ;
uc [ 0 ] = ( c [ 0 ] < < 6 ) | ( c [ 1 ] & 0x3f ) ;
len = 2 ;
}
( * inbuf ) + = len ;
( * inbytesleft ) - = len ;
( * outbytesleft ) - = 2 ;
( * outbuf ) + = 2 ;
}
if ( * inbytesleft > 0 ) {
errno = E2BIG ;
return - 1 ;
}
return 0 ;
badseq :
errno = EINVAL ;
return - 1 ;
}
2001-07-22 07:38:32 +00:00
static size_t utf8_push ( void * cd , char * * inbuf , size_t * inbytesleft ,
2001-07-04 15:08:30 +00:00
char * * outbuf , size_t * outbytesleft )
{
while ( * inbytesleft > = 2 & & * outbytesleft > = 1 ) {
unsigned char * c = ( unsigned char * ) * outbuf ;
unsigned char * uc = ( unsigned char * ) * inbuf ;
int len = 1 ;
2001-09-25 04:27:59 +00:00
if ( uc [ 1 ] & 0xf8 ) {
2001-07-04 15:08:30 +00:00
if ( * outbytesleft < 3 ) {
DEBUG ( 0 , ( " short utf8 write \n " ) ) ;
goto toobig ;
}
c [ 0 ] = 0xe0 | ( uc [ 1 ] > > 4 ) ;
c [ 1 ] = 0x80 | ( ( uc [ 1 ] & 0xF ) < < 2 ) | ( uc [ 0 ] > > 6 ) ;
c [ 2 ] = 0x80 | ( uc [ 0 ] & 0x3f ) ;
len = 3 ;
} else if ( uc [ 1 ] | ( uc [ 0 ] & 0x80 ) ) {
if ( * outbytesleft < 2 ) {
DEBUG ( 0 , ( " short utf8 write \n " ) ) ;
goto toobig ;
}
c [ 0 ] = 0xc0 | ( uc [ 1 ] < < 2 ) | ( uc [ 0 ] > > 6 ) ;
c [ 1 ] = 0x80 | ( uc [ 0 ] & 0x3f ) ;
len = 2 ;
} else {
c [ 0 ] = uc [ 0 ] ;
}
( * inbytesleft ) - = 2 ;
( * outbytesleft ) - = len ;
( * inbuf ) + = 2 ;
( * outbuf ) + = len ;
}
if ( * inbytesleft = = 1 ) {
errno = EINVAL ;
return - 1 ;
}
if ( * inbytesleft > 1 ) {
errno = E2BIG ;
return - 1 ;
}
return 0 ;
toobig :
errno = E2BIG ;
return - 1 ;
}