/*
 * webkernel_test.c
 *
 * Tests to stress the webkernel API.
 *
 * Author: Dario Rodriguez dario@softhome.net
 * This program is dual licensed: MIT and in "the public domain".
 */


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "sbuf.h"
#include "socklib.h"
#include "webkernel.h"

#define STRING_OK "ok"
#define STRING_FAIL "fail"

typedef enum test_action {
        test_name=0,
        test_description,
        test_run
} test_action;

char *test1(test_action action);
char *socklib_connect(test_action action);
char *socklib_sselect(test_action action);
char *sbuf_memory(test_action action);
char *webkernel_basic(test_action action);
char *webkernel_dual(test_action action);
char *webkernel_urldecode(test_action action);


struct {
        char *(*test)(/* test_action action */);
} tests[]={{test1},
           {socklib_connect},
           {socklib_sselect},
           {sbuf_memory},
           {webkernel_basic},
           {webkernel_dual},
           {webkernel_urldecode},
          };

int
main(int argc, char *argv[])
{
        int i;
        int flagall;
        char *resstr;
        int total,totalfail;
        int n;
        char *test;
        if(argc==1 || strcmp(argv[argc-1],"--help")==0 || argc<2) {
                printf("Syntax:\n\t%s { --help | --list | all | test_name [ test_name [...]] }\nNOTE: test_name is one of the tests listed in --list\n",argv[0]);
                return(0);
        }
        if(strcmp(argv[argc-1],"--list")==0) {
                for(i=0;i<(sizeof(tests)/sizeof(tests[0]));i++)
                        printf("%s\n\t%s\n",tests[i].test(test_name),tests[i].test(test_description));
                return(0);
        }
        flagall=(strcmp(argv[argc-1],"all")==0)?1:0;
        total=totalfail=0;
        for(n=(flagall)?0:1;(flagall==0 && n<argc) || (flagall!=0 && n<(sizeof(tests)/sizeof(tests[0])));n++) {
                test=(flagall!=0)?tests[n].test(test_name):argv[n];
                for(i=0;i<(sizeof(tests)/sizeof(tests[0])) && strcmp(tests[i].test(test_name),test)!=0;i++)
                        ;
                if(i>=(sizeof(tests)/sizeof(tests[0])))
                        continue;
                printf("%20s...",tests[i].test(test_name));
                fflush(stdout);
                resstr=tests[i].test(test_run);
                total++;
                totalfail+=((memcmp(resstr,STRING_OK,strlen(STRING_OK))==0)?0:1);
                printf("%s\n",resstr);

        }
        if(total==0) {
                printf("ERROR: test not found\n");
                return(1);
        }
        if(totalfail!=0)
                printf("Failed %i of %i tests.\n",totalfail,total);
        else
                printf("Passed %i of %i tests.\n",total,total);
        return((totalfail!=0)?1:0);
}

/* test1 */
char *
test1(test_action action)
{
        if(action==test_name)
                return("test1");
        else if(action==test_description)
                return("test the testing framework");
        /* run test */
        return("ok");
}

/* socklib_connect */
char *
socklib_connect(test_action action)
{
        int server,client;
        char *host;
        long hostsize;
        int port;
        int off;
        int timeout=100;
        if(action==test_name)
                return("socklib_connect");
        if(action==test_description)
                return("listen and connect");
        if((host=ipv4_genip("localhost",&hostsize))==NULL)
                return(STRING_FAIL ": couldn't resove localhost");
        for(server=-1,off=0,port=19747;off<1024;off++,port++) {
                if((server=ipv4_serverbinded(host,hostsize,port))!=-1)
                        break;
        }
        if(server==-1) {
                free(host),host=NULL;
                return(STRING_FAIL ": couldn't find empty port for server\n");
        }
        sock_setunsafe(server);
        if((client=ipv4_preconnect(host,hostsize,port))==-1) {
                close(server),server=-1;
                free(host),host=NULL;
                return(STRING_FAIL ": couldn't connect to server\n");
        }
        if(ipv4_connect(client,timeout)==-1) {
                close(client),client=-1;
                close(server),server=-1;
                free(host),host=NULL;
                return(STRING_FAIL ": timeout on connect\n");
        }
        ipv4_postconnect(client);
        close(client),client=-1;
        close(server),server=-1;
        free(host),host=NULL;
        return(STRING_OK);
}

/* socklib_sselect */
char *
test_socketsinit(int *server, int *client, char **host, long *hostsize, int *port)
{
        int timeout=100;
        int off;
        *server=*client=*port=-1;
        *hostsize=0;
        *host=NULL;
        *port=-1;
        if((*host=ipv4_genip("localhost",hostsize))==NULL)
                return(STRING_FAIL ": couldn't resove localhost");
        for(*server=-1,off=0,*port=19747;off<1024;off++,(*port)++) {
                if((*server=ipv4_serverbinded(*host,*hostsize,*port))!=-1)
                        break;
        }
        if(*server==-1) {
                free(*host),*host=NULL;
                return(STRING_FAIL ": couldn't find empty port for server\n");
        }
        sock_setunsafe(*server);
        if((*client=ipv4_preconnect(*host,*hostsize,*port))==-1) {
                close(*server),*server=-1;
                free(*host),*host=NULL;
                return(STRING_FAIL ": couldn't connect to server\n");
        }
        if(ipv4_connect(*client,timeout)==-1) {
                close(*client),*client=-1;
                close(*server),*server=-1;
                free(*host),*host=NULL;
                return(STRING_FAIL ": timeout on connect\n");
        }
        ipv4_postconnect(*client);
        return(NULL);
}

void
test_socketsfini(int *server, int *client, char **host, long *hostsize, int *port)
{
        if(*server!=-1)
                close(*server),*server=-1;
        if(*client!=-1)
                close(*client),*client=-1;
        if(*host!=NULL)
                free(*host),*host=NULL;
        *hostsize=0;
        *port=-1;
}

char *
socklib_sselect(test_action action)
{
        int server,client;
        char *host;
        long hostsize;
        int port;
        char *result;
        char buf[128];
        int readyfds[16];
        sselect *ssel;
        int timeout=100;
        int workfd;
        if(action==test_name)
                return("socklib_sselect");
        if(action==test_description)
                return("select over sockets");
        if((result=test_socketsinit(&server,&client,&host,&hostsize,&port))!=NULL) {
                test_socketsfini(&server,&client,&host,&hostsize,&port);
                return(result);
        }
        if((ssel=sselect_init())==NULL) {
                test_socketsfini(&server,&client,&host,&hostsize,&port);
                return(STRING_FAIL ": couldn't init sselect struct");
        }
        if(sselect_reset(ssel)!=0) {
                sselect_free(ssel),ssel=NULL;
                test_socketsfini(&server,&client,&host,&hostsize,&port);
                return(STRING_FAIL ": couldn't test the sselect reset function");
        }
        if(sselect_wait(ssel,timeout)!=0) {
                sselect_free(ssel),ssel=NULL;
                test_socketsfini(&server,&client,&host,&hostsize,&port);
                return(STRING_FAIL ": (1) was expecting a timeout, something else happened");
        }
        if(sselect_addread(ssel,server,buf)!=0) {
                sselect_free(ssel),ssel=NULL;
                test_socketsfini(&server,&client,&host,&hostsize,&port);
                return(STRING_FAIL ": couldn't add 'read serverfd' to the sselect");
        }
        if(sselect_wait(ssel,timeout)!=1) {
                sselect_free(ssel),ssel=NULL;
                test_socketsfini(&server,&client,&host,&hostsize,&port);
                return(STRING_FAIL ": (2) was expecting a ready-to-read, something else happened");
        }
        if((workfd=sock_accept(server))==-1) {
                sselect_free(ssel),ssel=NULL;
                test_socketsfini(&server,&client,&host,&hostsize,&port);
                return(STRING_FAIL ": couldn't accept connection");
        }
        write(client,".",1);
        if(sselect_wait(ssel,timeout)!=0) {
                sselect_free(ssel),ssel=NULL;
                test_socketsfini(&server,&client,&host,&hostsize,&port);
                return(STRING_FAIL ": (2) was expecting a timeout, something else happened");
        }
        if(sselect_addread(ssel,workfd,&workfd)!=0) {
                sselect_free(ssel),ssel=NULL;
                close(workfd),workfd=-1;
                test_socketsfini(&server,&client,&host,&hostsize,&port);
                return(STRING_FAIL ": couldn't add 'read workfd' to the sselect");
        }
        if(sselect_wait(ssel,timeout)!=1) {
                sselect_free(ssel),ssel=NULL;
                close(workfd),workfd=-1;
                test_socketsfini(&server,&client,&host,&hostsize,&port);
                return(STRING_FAIL ": was expecting a something-to-read, something else happened");
        }
        if((sselect_getread(ssel,readyfds,sizeof(readyfds)/sizeof(readyfds[0])))!=1 || *readyfds!=workfd) {
                sselect_free(ssel),ssel=NULL;
                close(workfd),workfd=-1;
                test_socketsfini(&server,&client,&host,&hostsize,&port);
                return(STRING_FAIL ": the list of ready-to-read fds is wrong");
        }
        if(sselect_getuserptr(ssel,workfd)!=&workfd) {
                sselect_free(ssel),ssel=NULL;
                close(workfd),workfd=-1;
                test_socketsfini(&server,&client,&host,&hostsize,&port);
                return(STRING_FAIL ": Couldn't recover the userptr of workfd");
        }
        read(workfd,buf,1);
        if(sselect_addwrite(ssel,client,buf)!=0) {
                sselect_free(ssel),ssel=NULL;
                close(workfd),workfd=-1;
                test_socketsfini(&server,&client,&host,&hostsize,&port);
                return(STRING_FAIL ": couldn't add 'write clientfd' to the sselect");
        }
        if(sselect_wait(ssel,timeout)!=1) {
                sselect_free(ssel),ssel=NULL;
                close(workfd),workfd=-1;
                test_socketsfini(&server,&client,&host,&hostsize,&port);
                return(STRING_FAIL ": was expecting an ok-to-write, something else happened");
        }
        sselect_free(ssel),ssel=NULL;
        close(workfd),workfd=-1;
        test_socketsfini(&server,&client,&host,&hostsize,&port);
        return(STRING_OK);
}

/* sbuf_memory */
char *
sbuf_memory(test_action action)
{
        int bufsize=2048;
        const unsigned char data[]={0,2,4,1,2,4,1,76,91,147,135,253,121,56,5,9};
        unsigned char moredata[16];
        unsigned char *ptr;
        const char *line,lines[]={"one\nand two\nthree"};
        int i;
        sbuf *buf;
        if(action==test_name)
                return("sbuf_memory");
        else if(action==test_description)
                return("memory operations with sbuf");
        if((buf=sbuf_init(bufsize))==NULL)
                return(STRING_FAIL ": couldn't alloc new sbuf");
        if(sbuf_add(buf,(char *)data,sizeof(data))!=sizeof(data)) {
                sbuf_free(buf),buf=NULL;
                return(STRING_FAIL ": couldn't push data");
        }
        for(i=0;i<sizeof(moredata);i++)
                moredata[i]=i;
        if(sbuf_add(buf,(char *)moredata,sizeof(moredata))!=sizeof(moredata)) {
                sbuf_free(buf),buf=NULL;
                return(STRING_FAIL ": (2) couldn't push data");
        }
        if(sbuf_count(buf)!=(sizeof(data)+sizeof(moredata))) {
                sbuf_free(buf),buf=NULL;
                return(STRING_FAIL ": bad count");
        }
        for(i=0;i<sizeof(data);i++) {
                if((ptr=(unsigned char *)sbuf_getbytes(buf,1))==NULL || *ptr!=data[i]) {
                        sbuf_free(buf),buf=NULL;
                        return(STRING_FAIL ": bad retrieval");
                }
        }
        sbuf_discard(buf);
        if((ptr=(unsigned char *)sbuf_getbytes(buf,sizeof(moredata)))==NULL || memcmp(ptr,moredata,sizeof(moredata))!=0) {
                sbuf_free(buf),buf=NULL;
                return(STRING_FAIL ": (2) bad retrieval");
        }
        if(sbuf_getbytes(buf,1)!=NULL) {
                sbuf_free(buf),buf=NULL;
                return(STRING_FAIL ": (3) bad retrieval");
        }
        sbuf_discard(buf);
        if(sbuf_addstr(buf,lines)!=(sizeof(lines)-1)) {
                sbuf_free(buf),buf=NULL;
                return(STRING_FAIL ": (3) couldn't push data");
        }
        if((line=sbuf_getline(buf))==NULL || strcmp(line,"one")!=0) {
                sbuf_free(buf),buf=NULL;
                return(STRING_FAIL ": bad line retrieval");
        }
        if((line=sbuf_getline(buf))==NULL || strcmp(line,"and two")!=0) {
                sbuf_free(buf),buf=NULL;
                return(STRING_FAIL ": (2) bad line retrieval");
        }
        if(sbuf_getline(buf)!=NULL) {
                sbuf_free(buf),buf=NULL;
                return(STRING_FAIL ": (3) bad line retrieval");
        }
        sbuf_free(buf),buf=NULL;
        return(STRING_OK);
}

/* webkernel_basic */
wk_action
webkernel_basichttp(wk *web, int connid, wk_uri *uri, void *userptr)
{

        char reply_ok[]={"Ok"};
        if(strcmp(uri->path,"/test.cgi")==0) {
                (*((int *)userptr))=(*((int *)userptr))+1;
                wk_serve_buffer_as_file(web,connid,reply_ok,sizeof(reply_ok)-1,mime_getdefault("test.txt",NULL));
        }
        return(wkact_finished);
}

void
webkernel_basicclose(int *client,sselect **ssel, char **host, wk **web)
{
        if(*client!=-1)
                close(*client),*client=-1;
        if(*host!=NULL)
                free(*host),*host=NULL;
        if(*web!=NULL)
                wk_free(*web),*web=NULL;
        if(*ssel!=NULL)
                sselect_free(*ssel),*ssel=NULL;
}

char *
webkernel_basic(test_action action)
{
        wk *web;
        int port;
        int off;
        int check;
        sselect *ssel;
        int client;
        char *host;
        long hostsize;
        int timeout;
        int fds[2];
        char buf[128];
        int usedbuf;
        int serverfd;
        char test_request[]={"\
GET /test.cgi HTTP/1.0\r\n\
Host: localhost\r\n\
Connection: keep-alive\r\n\
\r\n\
"};
        if(action==test_name)
                return("webkernel_basic");
        else if(action==test_description)
                return("webkernel basic usage");
        client=-1;
        ssel=NULL;
        host=NULL;
        web=NULL;
        timeout=100;
        check=0;
        if((ssel=sselect_init())==NULL)
                return(STRING_FAIL ": couln't alloc a sselect structure");
        for(port=19747,off=0,check=0;(serverfd=ipv4_server(port))==-1;off++,port++)
                ;
        sock_setunsafe(serverfd);
        if((web=wk_init(serverfd,ssel,NULL,webkernel_basichttp,NULL,NULL,&check))==NULL) {
                webkernel_basicclose(&client,&ssel,&host,&web);
                return(STRING_FAIL ": couln't find a free port to init webkernel");
        }
        if((host=ipv4_genip("localhost",&hostsize))==NULL) {
                webkernel_basicclose(&client,&ssel,&host,&web);
                return(STRING_FAIL ": couldn't resove localhost");
        }
        if((client=ipv4_preconnect(host,hostsize,port))==-1) {
                webkernel_basicclose(&client,&ssel,&host,&web);
                return(STRING_FAIL ": couldn't connect to server\n");
        }
        if(ipv4_connect(client,timeout)==-1) {
                webkernel_basicclose(&client,&ssel,&host,&web);
                return(STRING_FAIL ": timeout on connect\n");
        }
        ipv4_postconnect(client);
        /* accept the connection */
        if(sselect_wait(ssel,timeout)!=1) {
                webkernel_basicclose(&client,&ssel,&host,&web);
                return(STRING_FAIL ": timeout on sselect_wait\n");
        }
        wk_service(web);
        if(sselect_getread(ssel,fds,sizeof(fds)/sizeof(fds[0]))==1) {
                webkernel_basicclose(&client,&ssel,&host,&web);
                return(STRING_FAIL ": wk_service hasn't serviced the server connection\n");
        }
        /* client does request into socket */
        write(client,test_request,sizeof(test_request)-1);
        /* socket-> callback_http -> reply_in_wk */
        if(sselect_wait(ssel,timeout)!=1) {
                webkernel_basicclose(&client,&ssel,&host,&web);
                return(STRING_FAIL ": timeout on sselect_wait (after client request)\n");
        }
        wk_service(web);
        if(check!=1) {
                webkernel_basicclose(&client,&ssel,&host,&web);
                return(STRING_FAIL ": http callback was not called\n");
        }
        /* reply_in_wk -> socket */
        if(sselect_wait(ssel,timeout)!=1) {
                webkernel_basicclose(&client,&ssel,&host,&web);
                return(STRING_FAIL ": (2) timeout on sselect_wait (after client request)\n");
        }
        wk_service(web);
        /* client read availability */
        sselect_addread(ssel,client,NULL);
        if(sselect_wait(ssel,timeout)!=1) {
                webkernel_basicclose(&client,&ssel,&host,&web);
                return(STRING_FAIL ": timeout on sselect_wait (to read client reply)\n");
        }
        wk_service(web);
        if(sselect_getread(ssel,fds,sizeof(fds)/sizeof(fds[0]))!=1 || *fds!=client) {
                webkernel_basicclose(&client,&ssel,&host,&web);
                return(STRING_FAIL ": client read not detected\n");
        }
        /* read data */
        if((usedbuf=read(client,buf,sizeof(buf)-1))<0) {
                webkernel_basicclose(&client,&ssel,&host,&web);
                return(STRING_FAIL ": couldn't read reply\n");
        }
        buf[usedbuf]='\0';
        if(usedbuf<2 || memcmp(buf+usedbuf-2,"Ok",2)!=0) {
                webkernel_basicclose(&client,&ssel,&host,&web);
                return(STRING_FAIL ": corrupted data in read on client\n");
        }
        close(client),client=-1;
        wk_free(web),web=NULL;
        close(serverfd),serverfd=-1;
        sselect_free(ssel),ssel=NULL;
        free(host),host=NULL;
        return(STRING_OK);
}

/* webkernel_dual */
void
webkernel_dualclose(int *clients,int sizeclients,sselect **ssel, char **host, wk **web)
{
        int c;
        for(c=0;c<sizeclients;c++) {
                if(clients[c]!=-1)
                        close(clients[c]),clients[c]=-1;
        }
        if(*host!=NULL)
                free(*host),*host=NULL;
        if(*web!=NULL)
                wk_free(*web),*web=NULL;
        if(*ssel!=NULL)
                sselect_free(*ssel),*ssel=NULL;
}

char *
webkernel_dual(test_action action)
{
        wk *web;
        int port;
        int off;
        int check;
        sselect *ssel;
        int serverfd;
        int clients[2];
        int c;
        char *host;
        long hostsize;
        int timeout;
        int fds[2];
        char buf[128];
        int usedbuf;
        char test_request[]={"\
GET /test.cgi HTTP/1.0\r\n\
Host: localhost\r\n\
Connection: keep-alive\r\n\
\r\n\
"};
        if(action==test_name)
                return("webkernel_dual");
        else if(action==test_description)
                return("webkernel multiple simultaneous connections");
        for(c=0;c<(sizeof(clients)/sizeof(clients[0]));c++)
                clients[c]=-1;
        ssel=NULL;
        host=NULL;
        web=NULL;
        timeout=100;
        check=0;
        if((ssel=sselect_init())==NULL)
                return(STRING_FAIL ": couln't alloc a sselect structure");
        for(port=19747,off=0;(serverfd=ipv4_server(port))==-1;off++,port++)
                ;
        sock_setunsafe(serverfd);
        if((web=wk_init(serverfd,ssel,NULL,webkernel_basichttp,NULL,NULL,&check))==NULL) {
                webkernel_dualclose(clients,sizeof(clients)/sizeof(clients[0]),&ssel,&host,&web);
                return(STRING_FAIL ": couln't find a free port to init webkernel");
        }
        if((host=ipv4_genip("localhost",&hostsize))==NULL) {
                webkernel_dualclose(clients,sizeof(clients)/sizeof(clients[0]),&ssel,&host,&web);
                return(STRING_FAIL ": couldn't resove localhost");
        }
        for(c=0;c<(sizeof(clients)/sizeof(clients[0]));c++) {
                if((clients[c]=ipv4_preconnect(host,hostsize,port))==-1) {
                        webkernel_dualclose(clients,sizeof(clients)/sizeof(clients[0]),&ssel,&host,&web);
                        return(STRING_FAIL ": couldn't connect to server\n");
                }
                if(ipv4_connect(clients[c],timeout)==-1) {
                        webkernel_dualclose(clients,sizeof(clients)/sizeof(clients[0]),&ssel,&host,&web);
                                return(STRING_FAIL ": timeout on connect\n");
                }
                ipv4_postconnect(clients[c]);
        }
        /* accept the connection */
        if(sselect_wait(ssel,timeout)!=1) {
                webkernel_dualclose(clients,sizeof(clients)/sizeof(clients[0]),&ssel,&host,&web);
                return(STRING_FAIL ": timeout on sselect_wait\n");
        }
        wk_service(web);
        if(sselect_getread(ssel,fds,sizeof(fds)/sizeof(fds[0]))==1) {
                webkernel_dualclose(clients,sizeof(clients)/sizeof(clients[0]),&ssel,&host,&web);
                return(STRING_FAIL ": wk_service hasn't serviced the server connection\n");
        }
        /* client does request into socket */
        for(c=0;c<(sizeof(clients)/sizeof(clients[0]));c++)
                write(clients[c],test_request,sizeof(test_request)-1);
        /* socket-> callback_http -> reply_in_wk */
        if(sselect_wait(ssel,timeout)!=2) {
                webkernel_dualclose(clients,sizeof(clients)/sizeof(clients[0]),&ssel,&host,&web);
                return(STRING_FAIL ": timeout on sselect_wait (after client request)\n");
        }
        wk_service(web);
        if(check!=2) {
                webkernel_dualclose(clients,sizeof(clients)/sizeof(clients[0]),&ssel,&host,&web);
                return(STRING_FAIL ": http callback was not called\n");
        }
        /* reply_in_wk -> socket */
        if(sselect_wait(ssel,timeout)!=2) {
                webkernel_dualclose(clients,sizeof(clients)/sizeof(clients[0]),&ssel,&host,&web);
                return(STRING_FAIL ": (2) timeout on sselect_wait (after client request)\n");
        }
        wk_service(web);
        /* client read availability */
        for(c=0;c<(sizeof(clients)/sizeof(clients[0]));c++)
                sselect_addread(ssel,clients[c],NULL);
        if(sselect_wait(ssel,timeout)!=2) {
                webkernel_dualclose(clients,sizeof(clients)/sizeof(clients[0]),&ssel,&host,&web);
                return(STRING_FAIL ": timeout on sselect_wait (to read client reply)\n");
        }
        wk_service(web);
        if(sselect_getread(ssel,fds,sizeof(fds)/sizeof(fds[0]))!=2) {
                webkernel_dualclose(clients,sizeof(clients)/sizeof(clients[0]),&ssel,&host,&web);
                return(STRING_FAIL ": client read not detected\n");
        }
        /* read data */
        for(c=0;c<(sizeof(clients)/sizeof(clients[0]));c++) {
                if((usedbuf=read(clients[c],buf,sizeof(buf)-1))<0) {
                        webkernel_dualclose(clients,sizeof(clients)/sizeof(clients[0]),&ssel,&host,&web);
                        return(STRING_FAIL ": couldn't read reply\n");
                }
                buf[usedbuf]='\0';
                if(usedbuf<2 || memcmp(buf+usedbuf-2,"Ok",2)!=0) {
                        webkernel_dualclose(clients,sizeof(clients)/sizeof(clients[0]),&ssel,&host,&web);
                        return(STRING_FAIL ": corrupted data in read on client\n");
                }
        }
        for(c=0;c<(sizeof(clients)/sizeof(clients[0]));c++)
                close(clients[c]),clients[c]=-1;
        wk_free(web),web=NULL;
        close(serverfd),serverfd=-1;
        sselect_free(ssel),ssel=NULL;
        free(host),host=NULL;
        return(STRING_OK);
}

/* webkernel_urldecode */
char *
webkernel_urldecode(test_action action)
{
	static struct {
		char *encoded;
		char *decoded;
	} strs[]={{"my%40emailservice.com","my@emailservice.com"}};
	int i;
	char buf[1024];
	static char myerror[1024];
        if(action==test_name)
                return("webkernel_urldecode");
        else if(action==test_description)
                return("check some strings against uri_urldecode");
        /* run test */
	for(i=0;i<(sizeof(strs)/sizeof(strs[0]));i++) {
		strncpy(buf,strs[i].encoded,sizeof(buf));
		buf[sizeof(buf)-1]='\0';
		uri_urldecode(buf);
		if(strcmp(buf,strs[i].decoded)!=0) {
			snprintf(myerror,sizeof(myerror),"%s: incorrect decoding of string \"%s\" (\"%s\"!=\"%s\")\n",STRING_FAIL,strs[i].encoded,buf,strs[i].decoded);
			myerror[sizeof(myerror)-1]='\0';
			return(myerror);
		}
	}
        return(STRING_OK);
}