diff src/vbuf.c @ 43:f6db1f060a95

start on outlook 2003 64 bit format
author carl
date Sun, 06 Jan 2008 14:47:06 -0800
parents
children b12f4e50e2e8
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/vbuf.c	Sun Jan 06 14:47:06 2008 -0800
@@ -0,0 +1,932 @@
+// {{{ includes
+
+#include <ctype.h>
+//#include "defines.h"
+#include <errno.h>
+#include <iconv.h>
+#include <malloc.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "vbuf.h"
+#include "generic.h"
+
+#ifdef WITH_DMALLOC
+#include <dmalloc.h>
+#endif
+
+// }}}
+
+int skip_nl( char *s ) // {{{ returns the width of the newline at s[0]
+{
+        if( s[0] == '\n' ) return 1;
+        if( s[0] == '\r' && s[1] == '\n' ) return 2;
+        if( s[0] == '\0' ) return 0;
+        return -1;
+} // }}}
+int find_nl( vstr *vs ) // {{{ find newline of type type in b
+{
+        char *nextr, *nextn;
+
+        nextr = memchr( vs->b, '\r', vs->dlen );
+        nextn = memchr( vs->b, '\n', vs->dlen );
+
+        //case 1: UNIX, we find \n first
+        if( nextn && (nextr == NULL || nextr > nextn ) ) {
+                return nextn - vs->b;
+        }
+
+        //case 2: DOS, we find \r\n
+        if( NULL != nextr && NULL != nextn && 1 == (char*)nextn - (char*)nextr ) {
+                return nextr - vs->b;
+        }
+
+        //case 3: we find nothing
+
+        return -1;
+} // }}}
+
+// {{{ UTF8 <-> UTF16 <-> ISO8859 Character set conversion functions and (ack) their globals
+
+//TODO: the following should not be
+char *wwbuf=NULL;
+size_t nwwbuf=0;
+static int unicode_up=0;
+iconv_t i16to8, i8to16, i8859_1to8, i8toi8859_1;
+
+void unicode_init() // {{{
+{
+	char *wipe = "";
+	char dump[4];
+
+	if( unicode_up ) unicode_close();
+
+	if( (iconv_t)-1 == (i16to8 = iconv_open( "UTF-8", "UTF-16" ) ) ) {
+		fprintf(stderr, "doexport(): Couldn't open iconv descriptor for UTF-16 to UTF-8.\n");
+		exit( 1 );
+	}
+
+	if( (iconv_t)-1 == (i8to16 = iconv_open( "UTF-16", "UTF-8" ) ) ) {
+		fprintf(stderr, "doexport(): Couldn't open iconv descriptor for UTF-8 to UTF-16.\n");
+		exit( 2 );
+	}
+
+	//iconv will prefix output with an FF FE (utf-16 start seq), the following dumps that.
+	memset( dump, 'x', 4 );
+	ASSERT( 0 == utf8to16( wipe, 1, dump, 4 ), "unicode_init(): attempt to dump FF FE failed." );
+
+	if( (iconv_t)-1 == (i8859_1to8 = iconv_open( "UTF-8", "ISO_8859-1" ) ) ) {
+		fprintf(stderr, "doexport(): Couldn't open iconv descriptor for ASCII to UTF-8.\n");
+		exit( 1 );
+	}
+
+
+	if( (iconv_t)-1 == (i8toi8859_1 = iconv_open( "ISO_8859-1", "UTF-8" ) ) ) {
+		fprintf(stderr, "doexport(): Couldn't open iconv descriptor for UTF-8 to ASCII.\n");
+		exit( 1 );
+	}
+
+	unicode_up = 1;
+}
+// }}}
+void unicode_close() // {{{
+{
+	unicode_up = 0;
+	iconv_close( i8to16 );
+	iconv_close( i16to8 );
+	iconv_close( i8859_1to8 );
+	iconv_close( i8toi8859_1 );
+}
+// }}}
+
+//int utf16_write( FILE* stream, const void *buf, size_t count ) // {{{ write utf-8 or iso_8869-1 to stream after converting it to utf-16
+//{
+//
+//	//TODO: if anything big comes through here we are sunk, should do it
+//	//bit-by-bit, not one-big-gulp
+//	
+//	size_t inbytesleft, outbytesleft;
+//	char *inbuf, *outbuf;
+//	size_t icresult;
+//	size_t rl;
+//
+//	//do we have enough buffer space?
+//	if( !wwbuf || nwwbuf < (count * 2 + 2) ) {
+//		wwbuf = F_REALLOC( wwbuf, count * 2 +2 ); 
+//
+//		nwwbuf = count * 2 + 2;
+//	}
+//
+//	inbytesleft = count; outbytesleft = nwwbuf;
+//	inbuf = (char*)buf; outbuf = wwbuf;
+//
+////	fprintf(stderr, "X%s, %dX", (char*)buf, strlen( buf ));
+////	fflush(stderr);
+//
+//	if( (rl = strlen( buf ) + 1) != count ) {
+//		fprintf(stderr, "utf16_write(): reported buffer size (%d) does not match string length (%d)\n",
+//				count,
+//				rl);
+//
+//		//hexdump( (char*)buf, 0, count, 1 );
+//
+//		raise( SIGSEGV );
+//		inbytesleft = rl;
+//	}
+//
+////	fprintf(stderr, "  attempting to convert:\n");
+////	hexdump( (char*)inbuf, 0, count, 1 );
+//
+//	icresult = iconv( i8to16, &inbuf, &inbytesleft, &outbuf, &outbytesleft );
+//
+////	fprintf(stderr, "  converted:\n");
+////	hexdump( (char*)buf, 0, count, 1 );
+//
+////	fprintf(stderr, "  to:\n");
+////	hexdump( (char*)wwbuf, 0, nwwbuf, 1 );
+//
+//	if( (size_t)-1  == icresult ) {
+//		fprintf(stderr, "utf16_write(): iconv failure(%d): %s\n", errno, strerror( errno ) );
+//		fprintf(stderr, "  attempted to convert:\n");
+//		hexdump( (char*)inbuf, 0, count, 1 );
+//
+//		fprintf(stderr, "  result:\n");
+//		hexdump( (char*)outbuf, 0, count, 1 );
+//
+//		fprintf(stderr, "I'm going to segfault now.\n");
+//		raise( SIGSEGV );
+//		exit(1);
+//	}
+//
+//	if( inbytesleft > 0 ) {
+//		fprintf(stderr, "utf16_write(): iconv returned a short count.\n");
+//		exit(1);
+//	}
+//
+//	return fwrite( wwbuf, nwwbuf - outbytesleft - 2, 1, stream  );
+//}
+// }}}
+
+//char *utf16buf = NULL;
+//int utf16buf_len = 0;
+//
+//int utf16_fprintf( FILE* stream, const char *fmt, ... ) // {{{
+//{
+//	int result=0;
+//	va_list ap;
+//
+//	if( utf16buf == NULL ) {
+//		utf16buf = (char*)F_MALLOC( SZ_MAX + 1 );
+//
+//		utf16buf_len = SZ_MAX + 1;
+//	}
+//
+//	va_start( ap, fmt );
+//
+//	result = vsnprintf( utf16buf, utf16buf_len, fmt, ap );
+//
+//	if( result + 1 > utf16buf_len ) { //didn't have space, realloc() and try again
+//		fprintf(stderr, "utf16_fprintf(): buffer too small (%d), F_MALLOC(%d)\n", utf16buf_len, result);
+//		free( utf16buf );
+//		utf16buf_len = result + 1;
+//		utf16buf = (char*)F_MALLOC( utf16buf_len );
+//
+//		result = vsnprintf( utf16buf, utf16buf_len, fmt, ap );
+//	}
+//
+//
+//	//didn't have space...again...something weird is going on...
+//	ASSERT( result + 1 <= utf16buf_len,  "utf16_fprintf(): Unpossible error!\n");
+//
+//	if( 1 != utf16_write( stream, utf16buf, result + 1 ) )
+//		DIE( "Write error?  -> %s or %s\n", strerror( errno ), uerr_str( uerr_get() ) );
+//
+//	return result;
+//}
+//// }}}
+//int utf16to8( char *inbuf_o, char *outbuf_o, int length ) // {{{
+//{
+//	int inbytesleft = length;
+//	int outbytesleft = length;
+//	char *inbuf = inbuf_o;
+//	char *outbuf = outbuf_o;
+//	int rlen = -1, tlen;
+//	int icresult = -1; 
+//
+//	int i, strlen=-1;
+//	
+//	DEBUG(
+//		fprintf(stderr, "  utf16to8(): attempting to convert:\n");
+//		//hexdump( (char*)inbuf_o, 0, length, 1 );
+//		fflush(stderr);
+//	);
+//
+//	for( i=0; i<length ; i+=2 ) {
+//		if( inbuf_o[i] == 0 && inbuf_o[i + 1] == 0 ) {
+//			//fprintf(stderr, "End of string found at: %d\n", i );
+//			strlen = i;
+//		}
+//	}
+//
+//	//hexdump( (char*)inbuf_o, 0, strlen, 1 );
+//
+//	if( -1 == strlen ) WARN("String is not zero-terminated.");
+//
+//	//iconv does not like it when the inbytesleft > actual string length
+//	//enum: zero terminated, length valid
+//	//      zero terminated, length short //we won't go beyond length ever, so this is same as NZT case
+//	//      zero terminated, length long
+//	//      not zero terminated
+//	//      TODO: MEMORY BUG HERE!
+//	for( tlen = 0; tlen <= inbytesleft - 2; tlen+=2 ) {
+//		if( inbuf_o[tlen] == 0 && inbuf_o[tlen+1] == 0 ){
+//			rlen = tlen + 2;
+//			tlen = rlen;
+//			break;
+//		}
+//		if( tlen == inbytesleft )fprintf(stderr, "Space allocated for string > actual string length.  Go windows!\n");
+//	}
+//
+//	if( rlen >= 0 )
+//		icresult = iconv( i16to8, &inbuf, &rlen, &outbuf, &outbytesleft );
+//
+//	if( icresult == (size_t)-1 ) {
+//		fprintf(stderr, "utf16to8(): iconv failure(%d): %s\n", errno, strerror( errno ) );
+//		fprintf(stderr, "  attempted to convert:\n");
+//		hexdump( (char*)inbuf_o, 0, length, 1 );
+//		fprintf(stderr, "  result:\n");
+//		hexdump( (char*)outbuf_o, 0, length, 1 );
+//		fprintf(stderr, "  MyDirtyOut:\n");
+//		for( i=0; i<length; i++) {
+//			if( inbuf_o[i] != '\0' ) fprintf(stderr, "%c", inbuf_o[i] );
+//		}
+//
+//		fprintf( stderr, "\n" );
+//		raise( SIGSEGV );
+//		exit(1);
+//	}
+//
+//	DEBUG(
+//		fprintf(stderr, "  result:\n");
+//		hexdump( (char*)outbuf_o, 0, length, 1 );
+//	     )
+//
+//	//fprintf(stderr, "utf16to8() returning %s\n", outbuf );
+//
+//	return icresult;	
+//}
+//// }}}
+int utf16_is_terminated( char *str, int length ) // {{{
+{
+	VSTR_STATIC( errbuf, 100 );
+	int len = -1;
+	int i;
+	for( i=0; i<length ; i+=2 ) {
+		if( str[i] == 0 && str[i + 1] == 0 ) {
+			//fprintf(stderr, "End of string found at: %d\n", i );
+			len = i;
+		}
+	}
+
+	//hexdump( (char*)inbuf_o, 0, len, 1 );
+
+	if( -1 == len ) {
+		vshexdump( errbuf, str, 0, length, 1 );
+		WARN("String is not zero terminated (probably broken data from registry) %s.", errbuf->b);
+	}
+
+	return (-1 == len )?0:1;
+} // }}}
+int vb_utf16to8( vbuf *dest, char *buf, int len ) // {{{
+{
+	int inbytesleft = len;
+	char *inbuf = buf;
+	//int rlen = -1, tlen;
+	int icresult = -1; 
+	VBUF_STATIC( dumpster, 100 );
+
+	//int i; //, strlen=-1;
+	int outbytesleft; 
+	char *outbuf;
+
+	ASSERT( unicode_up, "vb_utf16to8() called before unicode started." );
+
+	if( 2 > dest->blen ) vbresize( dest, 2 );
+	dest->dlen = 0;
+
+	//Bad Things can happen if a non-zero-terminated utf16 string comes through here
+	if( !utf16_is_terminated( buf, len ) ) return -1;
+
+	do {
+		outbytesleft = dest->blen - dest->dlen;
+		outbuf = dest->b + dest->dlen;
+		icresult = iconv( i16to8, &inbuf, &inbytesleft, &outbuf, &outbytesleft );
+		dest->dlen = outbuf - dest->b;
+		vbgrow( dest, inbytesleft);
+	} while( (size_t)-1 == icresult && E2BIG == errno );
+
+	if( 0 != vb_utf8to16T( dumpster, dest->b, dest->dlen ) )
+		DIE("Reverse conversion failed.");
+
+	if( icresult == (size_t)-1 ) {
+		//TODO: error
+		//ERR_UNIX( errno, "vb_utf16to8():iconv failure: %s", strerror( errno ) );
+		unicode_init();
+		return -1;
+		/*
+		fprintf(stderr, "  attempted to convert:\n");
+		hexdump( (char*)cin, 0, inlen, 1 );
+		fprintf(stderr, "  result:\n");
+		hexdump( (char*)bout->b, 0, bout->dlen, 1 );
+		fprintf(stderr, "  MyDirtyOut:\n");
+		for( i=0; i<inlen; i++) {
+			if( inbuf[i] != '\0' ) fprintf(stderr, "%c", inbuf[i] );
+		}
+
+		fprintf( stderr, "\n" );
+		raise( SIGSEGV );
+		exit(1);
+		*/
+	}
+
+	if( icresult ) {
+		//ERR_UNIX( EILSEQ, "Uhhhh...vb_utf16to8() returning icresult == %d", icresult );
+		return -1;
+	}
+	return icresult;	
+}
+// }}}
+
+int utf8to16( char *inbuf_o, int iblen, char *outbuf_o, int oblen) // {{{ iblen, oblen: bytes including \0
+{
+	//TODO: this is *only* used to dump the utf16 preamble now...
+	//TODO: This (and 8to16) are the most horrible things I have ever seen...
+	int inbytesleft;
+	int outbytesleft = oblen;
+	char *inbuf = inbuf_o;
+	char *outbuf = outbuf_o;
+	//int rlen = -1, tlen;
+	int icresult = -1; 
+
+	char *stend;
+
+	//int i; //, strlen=-1;
+	
+	DEBUG(
+		fprintf(stderr, "  utf8to16(): attempting to convert:\n");
+		//hexdump( (char*)inbuf_o, 0, length, 1 );
+		fflush(stderr);
+	);
+
+	stend = memchr( inbuf_o, '\0', iblen );
+	ASSERT( NULL != stend, "utf8to16(): in string not zero terminated." );
+
+	inbytesleft = ( stend - inbuf_o + 1 < iblen )? stend - inbuf_o + 1: iblen;
+
+	//iconv does not like it when the inbytesleft > actual string length
+	//enum: zero terminated, length valid
+	//      zero terminated, length short //we won't go beyond length ever, so this is same as NZT case
+	//      zero terminated, length long
+	//      not zero terminated
+	//      TODO: MEMORY BUG HERE!
+	//      
+	/*
+	for( tlen = 0; tlen <= inbytesleft - 2; tlen+=2 ) {
+		if( inbuf_o[tlen] == 0 && inbuf_o[tlen+1] == 0 ){
+			rlen = tlen + 2;
+			tlen = rlen;
+			break;
+		}
+		if( tlen == inbytesleft )fprintf(stderr, "Space allocated for string > actual string length.  Go windows!\n");
+	}
+	*/
+
+	//if( rlen >= 0 )
+		icresult = iconv( i8to16, &inbuf, &inbytesleft, &outbuf, &outbytesleft );
+
+	if( icresult == (size_t)-1 ) {
+		DIE("iconv failure(%d): %s\n", errno, strerror( errno ) );
+		//fprintf(stderr, "  attempted to convert:\n");
+		//hexdump( (char*)inbuf_o, 0, iblen, 1 );
+		//fprintf(stderr, "  result:\n");
+		//hexdump( (char*)outbuf_o, 0, oblen, 1 );
+		//fprintf(stderr, "  MyDirtyOut:\n");
+//		for( i=0; i<iblen; i++) {
+//			if( inbuf_o[i] != '\0' ) fprintf(stderr, "%c", inbuf_o[i] );
+//		}
+//
+//		fprintf( stderr, "\n" );
+//		raise( SIGSEGV );
+//		exit(1);
+	}
+
+//	DEBUG(
+//		fprintf(stderr, "  result:\n");
+//		hexdump( (char*)outbuf_o, 0, oblen, 1 );
+//	     )
+
+	//fprintf(stderr, "utf8to16() returning %s\n", outbuf );
+
+	//TODO: error
+	if( icresult ) printf("Uhhhh...utf8to16() returning icresult == %d\n", icresult );
+	return icresult;	
+}
+// }}}
+
+int vb_utf8to16T( vbuf *bout, char *cin, int inlen ) // {{{ 
+{
+	//TODO: This (and 8to16) are the most horrible things I have ever seen...
+	int inbytesleft = inlen;
+	char *inbuf = cin;
+	//int rlen = -1, tlen;
+	int icresult = -1; 
+
+	//int i; //, strlen=-1;
+	
+	//if( rlen >= 0 )
+	int outbytesleft; 
+	char *outbuf;
+	if( 2 > bout->blen ) vbresize( bout, 2 );
+	bout->dlen = 0;
+
+	do {
+		outbytesleft = bout->blen - bout->dlen;
+		outbuf = bout->b + bout->dlen;
+		icresult = iconv( i8to16, &inbuf, &inbytesleft, &outbuf, &outbytesleft );
+		bout->dlen = outbuf - bout->b;
+		vbgrow( bout, 20 );
+	} while( (size_t)-1 == icresult && E2BIG == errno );
+
+	if( icresult == (size_t)-1 ) {
+		WARN("iconv failure: %s", strerror( errno ) );
+		//ERR_UNIX( errno, "vb_utf8to16():iconv failure: %s", strerror( errno ) );
+		unicode_init();
+		return -1;
+		/*
+		fprintf(stderr, "vb_utf8to16(): iconv failure(%d == %d?): %s\n", errno, E2BIG, strerror( errno ) );
+		fprintf(stderr, "  attempted to convert:\n");
+		hexdump( (char*)cin, 0, inlen, 1 );
+		fprintf(stderr, "  result:\n");
+		hexdump( (char*)bout->b, 0, bout->dlen, 1 );
+		fprintf(stderr, "  MyDirtyOut:\n");
+		for( i=0; i<inlen; i++) {
+			if( inbuf[i] != '\0' ) fprintf(stderr, "%c", inbuf[i] );
+		}
+
+		fprintf( stderr, "\n" );
+		raise( SIGSEGV );
+		exit(1);
+		*/
+	}
+
+	//TODO: error
+	if( icresult ) printf("Uhhhh...vb_utf8to16() returning icresult == %d\n", icresult );
+	return icresult;	
+}
+// }}}
+#if 1
+void cheap_uni2ascii(char *src, char *dest, int l) /* {{{ Quick and dirty UNICODE to std. ascii */
+{
+
+	for (; l > 0; l -=2) {
+		*dest = *src;
+		dest++; src +=2;
+	}
+	*dest = 0;
+}
+// }}}
+#endif
+
+void cheap_ascii2uni(char *src, char *dest, int l) /* {{{ Quick and dirty ascii to unicode */
+{
+   for (; l > 0; l--) {
+      *dest++ = *src++;
+      *dest++ = 0;
+
+   }
+}
+// }}}
+
+// }}}
+// {{{ VARBUF Functions 
+vbuf *vballoc( size_t len ) // {{{
+{
+	struct varbuf *result;
+
+	result = F_MALLOC( sizeof( struct varbuf ) );
+
+	result->dlen = 0;
+	result->blen = 0;
+	result->buf = NULL;
+
+	vbresize( result, len );
+
+	return result;
+
+} // }}}
+void vbcheck( vbuf *vb ) // {{{
+{
+	ASSERT( vb->b - vb->buf <= vb->blen, "vbcheck(): vb->b outside of buffer range.");
+	ASSERT( vb->dlen <= vb->blen, "vbcheck(): data length > buffer length.");
+
+	ASSERT( vb->blen < 1024*1024, "vbcheck(): blen is a bit large...hmmm.");
+} // }}}
+void vbfree( vbuf *vb ) // {{{
+{
+	free( vb->buf );
+	free( vb );
+} // }}}
+void vbclear( struct varbuf *vb ) // {{{ditch the data, keep the buffer
+{
+	vbresize( vb, 0 );
+} // }}}
+void vbresize( struct varbuf *vb, size_t len ) // {{{ DESTRUCTIVELY grow or shrink buffer
+{
+	vb->dlen = 0;
+
+	if( vb->blen >= len ) {
+		vb->b = vb->buf;
+		return;
+	}
+
+	vb->buf = F_REALLOC( vb->buf, len );
+	vb->b = vb->buf;
+	vb->blen = len;
+} // }}}
+int vbavail( vbuf *vb ) // {{{
+{
+	return vb->blen - ((char*)vb->b - (char*)vb->buf + vb->dlen);
+} // }}}
+//void vbdump( vbuf *vb ) // {{{ TODO: to stdout?  Yuck
+//{
+//	printf("vb dump-------------\n");
+//        printf("dlen: %d\n", vb->dlen );
+//	printf("blen: %d\n", vb->blen );
+//	printf("b - buf: %d\n", vb->b - vb->buf );
+//	printf("buf:\n");
+//	hexdump( vb->buf, 0, vb->blen, 1 );
+//	printf("b:\n");
+//	hexdump( vb->b, 0, vb->dlen, 1 );
+//	printf("^^^^^^^^^^^^^^^^^^^^\n");
+//} // }}}
+void vbgrow( struct varbuf *vb, size_t len ) // {{{ out: vbavail(vb) >= len, data are preserved
+{
+	if( 0 == len ) return;
+
+	if( 0 == vb->blen ) {
+		vbresize( vb, len );
+		return;
+	}
+
+	if( vb->dlen + len > vb->blen ) {
+		if( vb->dlen + len < vb->blen * 1.5 ) len = vb->blen * 1.5;
+		char *nb = F_MALLOC( vb->blen + len );
+		//printf("vbgrow() got %p back from malloc(%d)\n", nb, vb->blen + len);
+		vb->blen = vb->blen + len;
+		memcpy( nb, vb->b, vb->dlen );
+
+		//printf("vbgrow() I am going to free %p\n", vb->buf );
+		free( vb->buf );
+		vb->buf = nb;
+		vb->b = vb->buf;
+	} else {
+		if( vb->b != vb->buf ) 
+			memcpy( vb->buf, vb->b, vb->dlen );
+	}
+
+	vb->b = vb->buf;
+
+	ASSERT( vbavail( vb ) >= len, "vbgrow(): I have failed in my mission." );
+} // }}}
+void vbset( vbuf *vb, void *b, size_t len ) // {{{ set vbuf b size=len, resize if necessary, relen = how much to over-allocate
+{
+	vbresize( vb, len );
+
+	memcpy( vb->b, b, len );
+	vb->dlen = len;
+} // }}}
+void vsskipws( vstr *vs ) // {{{
+{
+	char *p = vs->b;
+	while( p - vs->b < vs->dlen && isspace( p[0] ) ) p++;
+
+	vbskip( (vbuf*)vs, p - vs->b );
+} // }}}
+void vbappend( struct varbuf *vb, void *b, size_t len ) // {{{ append len bytes of b to vbuf, resize if necessary
+{
+	if( 0 == vb->dlen ) {
+		vbset( vb, b, len );
+		return;
+	}
+
+	vbgrow( vb, len );
+
+	memcpy( vb->b + vb->dlen, b, len );
+	vb->dlen += len;
+
+	//printf("vbappend() end: >%s/%d<\n", vbuf->b, vbuf->dlen );
+} // }}}
+void vbskip( struct varbuf *vb, size_t skip ) // {{{ dumps the first skip bytes from vbuf
+{
+	ASSERT( skip <= vb->dlen, "vbskip(): Attempt to seek past end of buffer." );
+	//memmove( vbuf->b, vbuf->b + skip, vbuf->dlen - skip );
+	vb->b += skip;
+	vb->dlen -= skip;
+} // }}}
+void vboverwrite( struct varbuf *vbdest, struct varbuf *vbsrc ) // {{{ overwrite vbdest with vbsrc
+{
+	vbresize( vbdest, vbsrc->blen );
+	memcpy( vbdest->b, vbsrc->b, vbsrc->dlen );
+	vbdest->blen = vbsrc->blen;
+	vbdest->dlen = vbsrc->dlen;
+} // }}}
+// }}}
+// {{{ VARSTR Functions
+vstr *vsalloc( size_t len ) // {{{
+{
+	vstr *result = (vstr*)vballoc( len + 1 );
+	vsset( result, "" );
+	return result;
+} // }}}
+char *vsstr( vstr *vs ) // {{{
+{
+	return vs->b;
+} // }}}
+size_t vslen( vstr *vs ) // {{{
+{
+	return strlen( vsstr( vs ));
+} // }}}
+void vsfree( vstr *vs ) // {{{
+{
+	vbfree( (vbuf*)vs );
+} // }}}
+void vscharcat( vstr *vb, int ch ) // {{{
+{
+	vbgrow( (vbuf*)vb, 1);
+	vb->b[vb->dlen-1] = ch;
+	vb->b[vb->dlen] = '\0';
+	vb->dlen++;
+} // }}}
+void vsnprepend( vstr *vb, char *str, size_t len ) // {{{ prependappend string str to vbuf, vbuf must already contain a valid string 
+{
+	ASSERT( vb->b[vb->dlen-1] == '\0', "vsncat(): attempt to append string to non-string.");
+	int sl = strlen( str );
+	int n = (sl<len)?sl:len;
+	//string append
+	vbgrow( (vbuf*)vb, n + 1 );
+	memmove( vb->b + n, vb->b, vb->dlen - 1 );
+	memcpy( vb->b, str, n );
+	//strncat( vb->b, str, n );
+
+	vb->dlen += n;
+	vb->b[ vb->dlen - 1 ] = '\0';
+} // }}}
+void vsskip( vstr *vs, size_t len ) // {{{ len < dlen-1 -> skip len chars, else DIE
+{
+	ASSERT( len < vs->dlen - 1, "Attempt to skip past end of string" );
+	vbskip( (vbuf*)vs, len );
+} // }}}
+int vsskipline( vstr *vs ) // {{{ in: vb->b == "stuff\nmore_stuff"; out: vb->b == "more_stuff"
+{
+	int nloff = find_nl( vs );
+	int nll = skip_nl( vs->b + nloff );
+
+	if( nloff < 0 ) {
+		//TODO: error
+		printf("vb_skipline(): there seems to be no newline here.\n");
+		return -1;
+	}
+	if( skip_nl < 0 ) {
+		//TODO: error
+		printf("vb_skipline(): there seems to be no newline here...except there should be. :P\n");
+		return -1;
+	}
+
+	memmove( vs->b, vs->b + nloff + nll, vs->dlen - nloff - nll );
+
+	vs->dlen -= nloff + nll;
+
+	return 0;
+} // }}}
+int vscatprintf( vstr *vs, char *fmt, ... ) // {{{
+{
+	int size;
+	va_list ap;
+
+	/* Guess we need no more than 100 bytes. */
+	//vsresize( vb, 100 );
+	if(!vs->b || vs->dlen == 0) {
+		vsset( vs, "" );
+	}
+
+	while (1) {
+		/* Try to print in the allocated space. */
+		va_start(ap, fmt);
+		size = vsnprintf (vs->b + vs->dlen - 1, vs->blen - vs->dlen, fmt, ap);
+		va_end(ap);
+
+		/* If that worked, return the string. */
+		if (size > -1 && size < vs->blen - vs->dlen ) {
+			vs->dlen += size;
+			return size;
+		}
+		/* Else try again with more space. */
+		if ( size >= 0 )    /* glibc 2.1 */
+			vbgrow( (vbuf*)vs, size+1 ); /* precisely what is needed */
+		else           /* glibc 2.0 */
+			vbgrow( (vbuf*)vs, vs->blen);
+	}
+} // }}}
+int vslast( vstr *vs ) // {{{ returns the last character stored in a vstr 
+{
+	if( vs->dlen < 1 ) return -1;
+	if( vs->b[vs->dlen-1] != '\0' ) return -1;
+	if( vs->dlen == 1 ) return '\0';
+	return vs->b[vs->dlen-2];
+} // }}}
+void vs_printf( vstr *vs, char *fmt, ... ) // {{{ print over vb
+{
+	int size;
+	va_list ap;
+
+	/* Guess we need no more than 100 bytes. */
+	vbresize( (vbuf*)vs, 100 );
+
+	while (1) {
+		/* Try to print in the allocated space. */
+		va_start(ap, fmt);
+		size = vsnprintf (vs->b, vs->blen, fmt, ap);
+		va_end(ap);
+
+		/* If that worked, return the string. */
+		if (size > -1 && size < vs->blen) {
+			vs->dlen = size + 1;
+			return;
+		}
+		/* Else try again with more space. */
+		if ( size >= 0 )    /* glibc 2.1 */
+			vbresize( (vbuf*)vs, size+1 ); /* precisely what is needed */
+		else           /* glibc 2.0 */
+			vbresize( (vbuf*)vs, vs->blen*2);
+	}
+} // }}}
+void vs_printfa( vstr *vs, char *fmt, ... ) // {{{ printf append to vs
+{
+	int size;
+	va_list ap;
+
+	if( vs->blen - vs->dlen < 50 )
+		vbgrow( (vbuf*)vs, 100 );
+
+	while (1) {
+		/* Try to print in the allocated space. */
+		va_start(ap, fmt);
+		size = vsnprintf (vs->b + vs->dlen - 1, vs->blen - vs->dlen + 1, fmt, ap);
+		va_end(ap);
+
+		/* If that worked, return the string. */
+		if (size > -1 && size < vs->blen) {
+			vs->dlen += size;
+			return;
+		}
+		/* Else try again with more space. */
+		if ( size >= 0 )    /* glibc 2.1 */
+			vbgrow( (vbuf*)vs, size+1 - vs->dlen ); /* precisely what is needed */
+		else           /* glibc 2.0 */
+			vbgrow( (vbuf*)vs, size );
+	}
+} // }}}
+void vshexdump( vstr *vs, char *b, size_t start, size_t stop, int ascii ) // {{{
+{
+	char c;
+	int diff,i;
+
+	while (start < stop ) {
+		diff = stop - start;
+		if (diff > 16) diff = 16;
+
+		vs_printfa(vs, ":%08X  ",start);
+
+		for (i = 0; i < diff; i++) {
+			if( 8 == i ) vs_printfa( vs, " " );
+			vs_printfa(vs, "%02X ",(unsigned char)*(b+start+i));
+		}
+		if (ascii) {
+			for (i = diff; i < 16; i++) vs_printfa(vs, "   ");
+			for (i = 0; i < diff; i++) {
+				c = *(b+start+i);
+				vs_printfa(vs, "%c", isprint(c) ? c : '.');
+			}
+		}
+		vs_printfa(vs, "\n");
+		start += 16;
+	}
+} // }}}
+void vsset( vstr *vs, char *s ) // {{{ Store string s in vs
+{
+	vsnset( vs, s, strlen( s ) );
+} // }}}
+void vsnset( vstr *vs, char *s, size_t n ) // {{{ Store string s in vs
+{
+	vbresize( (vbuf*)vs, n + 1 );
+	memcpy( vs->b, s, n);
+	vs->b[n] = '\0';
+	vs->dlen = n+1;
+} // }}}
+void vsgrow(      vstr *vs, size_t len ) // {{{ grow buffer by len bytes, data are preserved
+{
+	vbgrow( (vbuf*)vs, len );
+} // }}}
+size_t vsavail( vstr *vs ) // {{{
+{
+	return vbavail( (vbuf*)vs );
+} // }}}
+void vsnset16( vstr *vs, char *s, size_t len ) // {{{ Like vbstrnset, but for UTF16
+{
+	vbresize( (vbuf*)vs, len+1 );
+	memcpy( vs->b, s, len );
+	
+	vs->b[len] = '\0';
+	vs->dlen = len+1;
+	vs->b[len] = '\0';
+} // }}}
+void vscat( vstr *vs, char *str ) // {{{
+{
+	vsncat( vs, str, strlen(str ) );
+} // }}}
+int vscmp( vstr *vs, char *str ) // {{{
+{
+	return strcmp( vs->b, str );
+} // }}}
+void vsncat( vstr *vs, char *str, size_t len ) // {{{ append string str to vstr, vstr must already contain a valid string 
+{
+	ASSERT( vs->b[vs->dlen-1] == '\0', "vsncat(): attempt to append string to non-string.");
+	int sl = strlen( str );
+	int n = (sl<len)?sl:len;
+	//string append
+	vbgrow( (vbuf*)vs, n + 1 );
+	memcpy( vs->b + vs->dlen - 1, str, n );
+	//strncat( vs->b, str, n );
+
+	vs->dlen += n;
+	vs->b[ vs->dlen - 1 ] = '\0';
+} // }}}
+void vstrunc( vstr *v, int off ) // {{{ Drop chars [off..dlen] 
+{
+	if( off >= v->dlen - 1 ) return; //nothing to do
+	v->b[off] = '\0';
+	v->dlen = off + 1;
+}
+// }}}
+// }}}
+// {{{ User input
+// TODO: not sure how useful this stuff is here
+int fmyinput(char *prmpt, char *ibuf, int maxlen) /* {{{ get user input */
+{
+	printf("%s",prmpt);
+
+	fgets(ibuf,maxlen+1,stdin);
+
+	ibuf[strlen(ibuf)-1] = 0;
+
+	return(strlen(ibuf));
+}
+// }}}
+//}}}
+//
+//
+//{{{ String formatting and output to FILE *stream or just stdout, etc 
+// TODO: a lot of old, unused stuff in here
+void vswinhex8(vstr *vs, unsigned char *hbuf, int start, int stop, int loff ) // {{{ Produce regedit-style hex output */
+{
+	int i;
+	int lineflag=0;
+
+	for( i=start; i<stop; i++)
+	{
+		loff += vscatprintf( vs, "%02x", hbuf[i] );
+		if( i < stop - 1 ) {
+			loff+=vscatprintf( vs, "," );
+			switch( lineflag ) {
+				case 0:
+				if( loff >= 77) {
+					lineflag=1;
+					loff=0;
+					vscatprintf( vs, "\\%s  ", STUPID_CR );
+				}
+				break;
+				case 1:
+				if( loff >= 75 ) {
+					loff=0;
+					vscatprintf( vs, "\\%s  ", STUPID_CR );
+				}
+				break;
+			}
+			//	if( 24 < i || 0 == (i - 17) % 25 ) fprintf( stream, "\\\n  " );
+		}
+	}
+
+	//   fprintf( stream, "\n" );
+} // }}}