aboutsummaryrefslogtreecommitdiffstats
path: root/daemon
diff options
context:
space:
mode:
Diffstat (limited to 'daemon')
-rw-r--r--daemon/syslogd.c61
1 files changed, 56 insertions, 5 deletions
diff --git a/daemon/syslogd.c b/daemon/syslogd.c
index 165896a..5ff5401 100644
--- a/daemon/syslogd.c
+++ b/daemon/syslogd.c
@@ -16,6 +16,7 @@
*/
#include <ctype.h>
+#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -190,15 +191,65 @@ static struct string* try_convert_string( struct string* s, GIConv cd )
{
struct string *ret;
gchar *converted_str;
+ gchar *inbuf, *outbuf;
+ gsize inbytes_left, outbytes, outbytes_left;
+ gsize converted_str_len, converted_str_allocated;
- converted_str = g_convert_with_iconv( s->gstr->str, -1, cd, NULL, NULL, NULL );
- if( !converted_str )
+ converted_str_allocated = s->gstr->len + /* at least */ 1;
+ converted_str = g_malloc( converted_str_allocated );
+
+ inbuf = s->gstr->str;
+ inbytes_left = s->gstr->len;
+ outbytes = converted_str_allocated;
+ outbuf = converted_str;
+ converted_str_len = 0;
+
+ if( 0 == inbytes_left )
+ goto done;
+
+ for(;;)
{
- TRACE( "conversion error\n" );
- return string_addref( s );
+ size_t r;
+
+ outbytes_left = outbytes;
+
+ r = g_iconv( cd, &inbuf, &inbytes_left, &outbuf, &outbytes_left );
+ converted_str_len += outbytes - outbytes_left;
+ outbytes = outbytes_left;
+ if( r != -1 )
+ goto done;
+
+ switch( errno )
+ {
+ case E2BIG:
+ /* guess the right output length */
+ outbytes = inbytes_left *
+ /* average size of destination char: */
+ converted_str_len / (s->gstr->len - inbytes_left)
+ + 16; /* if the result of above expression too small */
+ converted_str_allocated += outbytes;
+ converted_str = g_realloc( converted_str, converted_str_allocated );
+ outbuf = converted_str + converted_str_len;
+ break;
+
+ case EILSEQ:
+ inbuf++;
+ inbytes_left--;
+ /* FIXME: skip invalid characters? or replace with some fallback? */
+ break;
+
+ case EINVAL:
+ goto done;
+
+ default:
+ TRACE( "unknown conversion error %d\n", errno );
+ g_free( converted_str );
+ return string_addref( s );
+ }
}
+done:
- ret = string_new( converted_str );
+ ret = string_new_len( converted_str, converted_str_len );
g_free( converted_str );
return ret;
}