diff --git a/libtransmission/bencode-test.c b/libtransmission/bencode-test.c index f1615d0bb..e3b10e30b 100644 --- a/libtransmission/bencode-test.c +++ b/libtransmission/bencode-test.c @@ -1,3 +1,4 @@ +#include #include #include "transmission.h" #include "bencode.h" @@ -150,6 +151,10 @@ testString( const char * str, int isGood ) check( err ); } else { check( !err ); +#if 0 + fprintf( stderr, "in: [%s]\n", str ); + fprintf( stderr, "out:\n%s", tr_bencSaveAsJSON(&val,NULL) ); +#endif check( end == (const uint8_t*)str + len ); saved = tr_bencSave( &val, &savedLen ); check( !strcmp( saved, str ) ); @@ -219,6 +224,8 @@ testParse( void ) return err; if(( err = testString( "d4:spaml1:a1:bee", TRUE ))) return err; + if(( err = testString( "d5:greenli1ei2ei3ee4:spamd1:ai123e3:keyi214eee", TRUE ))) + return err; if(( err = testString( "d9:publisher3:bob17:publisher-webpage15:www.example.com18:publisher.location4:homee", TRUE ))) return err; if(( err = testString( "d8:completei1e8:intervali1800e12:min intervali1800e5:peers0:e", TRUE ))) @@ -271,6 +278,16 @@ testParse( void ) return 0; } +static void +stripWhitespace( char * in ) +{ + char * out; + for( out=in; *in; ++in ) + if( !isspace( *in ) ) + *out++ = *in; + *out = '\0'; +} + static int testJSONSnippet( const char * benc_str, const char * expected ) { @@ -278,9 +295,11 @@ testJSONSnippet( const char * benc_str, const char * expected ) char * serialized; tr_bencLoad( benc_str, strlen( benc_str ), &top, NULL ); serialized = tr_bencSaveAsJSON( &top, NULL ); + stripWhitespace( serialized ); #if 0 -fprintf( stderr, " expected: [%s]\n", expected ); -fprintf( stderr, " got: [%s]\n", serialized ); +fprintf( stderr, "benc: %s\n", benc_str ); +fprintf( stderr, "json: %s\n", serialized ); +fprintf( stderr, "want: %s\n", expected ); #endif check( !strcmp( serialized, expected ) ); tr_free( serialized ); @@ -301,22 +320,22 @@ testJSON( void ) return val; benc_str = "d5:helloi1e5:worldi2ee"; - expected = "{ \"hello\": 1, \"world\": 2 }"; + expected = "{\"hello\":1,\"world\":2}"; if(( val = testJSONSnippet( benc_str, expected ))) return val; benc_str = "d5:helloi1e5:worldi2e3:fooli1ei2ei3ee"; - expected = "{ \"foo\": [ 1, 2, 3 ], \"hello\": 1, \"world\": 2 }"; + expected = "{\"foo\":[1,2,3],\"hello\":1,\"world\":2}"; if(( val = testJSONSnippet( benc_str, expected ))) return val; benc_str = "d5:helloi1e5:worldi2e3:fooli1ei2ei3ed1:ai0eee"; - expected = "{ \"foo\": [ 1, 2, 3, { \"a\": 0 } ], \"hello\": 1, \"world\": 2 }"; + expected = "{\"foo\":[1,2,3,{\"a\":0}],\"hello\":1,\"world\":2}"; if(( val = testJSONSnippet( benc_str, expected ))) return val; - benc_str = "d4:argsd6:statuslee6:result7:successe"; - expected = "{ \"args\": { \"status\": [ ] }, \"result\": \"success\" }"; + benc_str = "d4:argsd6:statusle7:status2lee6:result7:successe"; + expected = "{\"args\":{\"status\":[],\"status2\":[]},\"result\":\"success\"}"; if(( val = testJSONSnippet( benc_str, expected ))) return val; diff --git a/libtransmission/bencode.c b/libtransmission/bencode.c index 64db437a5..69390b416 100644 --- a/libtransmission/bencode.c +++ b/libtransmission/bencode.c @@ -23,7 +23,7 @@ *****************************************************************************/ #include -#include /* isdigit, isprint */ +#include /* isdigit, isprint, isspace */ #include #include #include @@ -1023,6 +1023,13 @@ struct jsonWalk struct evbuffer * out; }; +static void +jsonIndent( struct jsonWalk * data ) +{ + const int width = tr_list_size( data->parents ) * 4; + evbuffer_add_printf( data->out, "\n%*.*s", width, width, " " ); +} + static void jsonChildFunc( struct jsonWalk * data ) { @@ -1036,14 +1043,17 @@ jsonChildFunc( struct jsonWalk * data ) const int i = parentState->childIndex++; if( ! ( i % 2 ) ) evbuffer_add_printf( data->out, ": " ); - else + else { evbuffer_add_printf( data->out, ", " ); + jsonIndent( data ); + } break; } case TYPE_LIST: { ++parentState->childIndex; evbuffer_add_printf( data->out, ", " ); + jsonIndent( data ); break; } @@ -1085,14 +1095,14 @@ jsonStringFunc( const tr_benc * val, void * vdata ) for( it=val->val.s.s, end=it+val->val.s.i; it!=end; ++it ) { switch( *it ) { - case '"' : evbuffer_add_printf( data->out, "\\\"" ); break; - case '/' : evbuffer_add_printf( data->out, "\\/" ); break; - case '\b': evbuffer_add_printf( data->out, "\\b" ); break; - case '\f': evbuffer_add_printf( data->out, "\\f" ); break; - case '\n': evbuffer_add_printf( data->out, "\\n" ); break; - case '\r': evbuffer_add_printf( data->out, "\\n" ); break; - case '\t': evbuffer_add_printf( data->out, "\\n" ); break; - case '\\': evbuffer_add_printf( data->out, "\\\\" ); break; + case '"' : + case '/' : + case '\b': + case '\f': + case '\n': + case '\r': + case '\t': + case '\\': evbuffer_add_printf( data->out, "\\%c", *it ); break; default: { if( isascii( *it ) ) evbuffer_add_printf( data->out, "%c", *it ); @@ -1110,29 +1120,46 @@ jsonDictBeginFunc( const tr_benc * val, void * vdata ) { struct jsonWalk * data = vdata; jsonPushParent( data, val ); - evbuffer_add_printf( data->out, "{ " ); - if( !val->val.l.count ) - evbuffer_add_printf( data->out, " " ); + evbuffer_add_printf( data->out, "{" ); + if( val->val.l.count ) + jsonIndent( data ); } static void jsonListBeginFunc( const tr_benc * val, void * vdata ) { + const int nChildren = tr_bencListSize( val ); struct jsonWalk * data = vdata; jsonPushParent( data, val ); - evbuffer_add_printf( data->out, "[ " ); - if( !val->val.l.count ) - evbuffer_add_printf( data->out, " " ); + evbuffer_add_printf( data->out, "[" ); + if( nChildren ) + jsonIndent( data ); } static void jsonContainerEndFunc( const tr_benc * val, void * vdata ) { + size_t i; struct jsonWalk * data = vdata; - EVBUFFER_LENGTH( data->out ) -= 2; - if( tr_bencIsDict( val ) ) - evbuffer_add_printf( data->out, " }" ); - else /* list */ - evbuffer_add_printf( data->out, " ]" ); + char * str; + int emptyContainer = FALSE; + + /* trim out the trailing comma, if any */ + str = (char*) EVBUFFER_DATA( data->out ); + for( i=EVBUFFER_LENGTH( data->out )-1; i>0; --i ) { + if( isspace( str[i] ) ) continue; + if( str[i]==',' ) + EVBUFFER_LENGTH( data->out ) = i; + if( str[i]=='{' || str[i]=='[' ) + emptyContainer = TRUE; + break; + } + jsonPopParent( data ); + if( !emptyContainer ) + jsonIndent( data ); + if( tr_bencIsDict( val ) ) + evbuffer_add_printf( data->out, "}" ); + else /* list */ + evbuffer_add_printf( data->out, "]" ); jsonChildFunc( data ); } char* @@ -1153,6 +1180,8 @@ tr_bencSaveAsJSON( const tr_benc * top, int * len ) bencWalk( top, &walkFuncs, &data ); + if( EVBUFFER_LENGTH( data.out ) ) + evbuffer_add_printf( data.out, "\n" ); if( len != NULL ) *len = EVBUFFER_LENGTH( data.out ); ret = tr_strndup( (char*) EVBUFFER_DATA( data.out ), EVBUFFER_LENGTH( data.out ) );