/*
 *      client.c
 *      --------
 *
 * $Id: client.c,v 1.1 1993/08/24 11:20:37 jrxr1 Exp jrxr1 $
 *
 * based on abwho - the socket based version (By Richard Hayton)
 *
 */

/* #defines for nicer error messages 
   
   HAS_SYS_ERRLIST (non ansi. sun ? not ultrix)
   HAS_STRERROR    (ansi)
   */


/* make sure we only use one error routine ! */

#define HAS_NO_ERROR
#ifdef HAS_SYS_ERRLIST
#undef HAS_NO_ERROR
#undef HAS_STRERROR
#include <errno.h>
#endif
#ifdef HAS_STRERROR
#undef HAS_NO_ERROR
#include <errno.h>
#include <string.h>
#endif

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <signal.h>
#include <strings.h>

#define BUFSIZE         (1024)
#define TRUE               (1)
#define FALSE              (0)
#define CONNECT_TIME_OUT   (8)  /* Time out quickly on connect */
#define NORMAL_TIME_OUT   (60)  /* Time out the whole program if it */
                                /* takes longer than this once */
                                /* connected */
#define CMD_LINE_LEN     (256)

#define BIBLE_SERVER    "apus.cus.cam.ac.uk"
#define BIBLE_PORT      1313


char *progName;
char buf_out[CMD_LINE_LEN];
    
FILE *out;
int out_flag;                   /* Whether or not a file has been opened. */


char *getenv();

void
    connect_timed_out()
{
    fprintf(stderr,"%s: connect timeout - fatal\n", progName);
    exit(1);
}

void
    normal_time_out()
{
    fprintf(stderr,"%s: program timeout - fatal\n", progName);
    exit(1);
}

int read_len( int b1, int b2 )
{
    if (b1<0)
        b1+=256;
    if (b2<0)
        b2+=256;
    return b1+(256*b2);
}

int read_data( int sock )
{
    int i,j,k,len,in_len;
    char buf[1024];
    int b1,b2;
    int l;
            
    for ( i=0 ; (len = recv(sock, buf+i, sizeof(buf)-i, 0))>0 ; )
    {
        i+=len;

        if (i>=2)
            in_len=read_len(buf[1],buf[2]);
        else
            in_len=i+1;

        while (in_len<=i)
        {
            /* Print the data. */
            if (buf[0]==1)
            {
                if (fwrite(buf+3, in_len-3, 1, out) < 0)
                {
                    perror("fwrite");
                    exit(1);
                }
            }
            else if (buf[0]==2)
            {
                if (fwrite(buf+3, in_len-3, 1, stderr) < 0)
                {
                    perror("fwrite");
                    exit(1);
                }
            }
            else
            {
                fprintf(stderr,"Unknown channel number.  Bug "
                        "somewhere.\n");
                if (fwrite(buf+3, in_len-3, 1, stderr) < 0)
                {
                    perror("fwrite");
                    exit(1);
                }
            }  
            
            /* test if we have more data than we have just printed. */
            
            if (in_len==i)
            {                   /* No, just go back to read loop. */
                in_len=1;
                i=0;
            }
            
            else
            {                   /* Yes, shift data, and reset in_len. */
                for ( j=0 ; j+in_len<i ; j++ )
                    buf[j]=buf[j+in_len];
                i-=in_len;
                if (i>=2)
                    in_len=read_len(buf[1],buf[2]);
                else
                    in_len=i+1;
            }
        }
    }
    return 0;
}


int parse_cmd_line( int argc, char **argv )
{
    int i,j,k;
    int flag;                   /* Are we in an option list at the */
                                /* moment? */
    FILE *new_out;
    
    
    for ( i=1,j=0,flag=0 ; i<argc ; )
    {

        if ((!flag) && (argv[i][j]=='-'))
            flag=1;
        else if (flag)
        {
            switch (argv[i][j])
            {
            case 'O':           /* Output filename.  Need to open a */
                                /* file here.  But can leave the rest */
                                /* for now. */
                /* Now just move the pointer on until after the */
                /* filename. */
                for ( j++ ; (argv[i][j]==' ') || (argv[i][j]==0) ; j++ )
                    if (argv[i][j]==0)
                    {
                        i+=1;
                        if (i>=argc)
                            return -1; /*  No more to interpret, so */
                                       /*  output filename is missing. */
                        j=-1;
                    }

                /* At start of filename here.  grab it here to pick up */
                /* filename. */

                new_out=fopen(argv[i]+j,"w");
                if (new_out)
                {
                    if (out_flag)
                        fclose(out);
                    out=new_out;
                    out_flag=1;
                }
                else 
                {
                    fprintf(stderr,"Couldn't open file %s.\n"
                            "Ignoring output option.",argv[i]+j);
                }

                    
                i+=1;
                j=-1;
                flag=0;         /* We have got to the end of the set */
                                /* of options.  May have another set */
                                /* starting with '-' though. */
                break;
            case 'c':           /* We know that the next item is going */
                                /* to be a name so skip to the end of */
                                /* it, and switch off flag. */
                i+=1;
                j=-1;
                flag=0;
                break;
                                /* Don't do anything with any of the */
                                /* other options. */
            }
        }    
        j++;
        if ((i<argc) && (!argv[i][j]))
        {
            j=0;
            i++;
            flag=0;
        }

    }
    /* Need to copy command line into guaranteed line of code. */
    for ( i=0,k=0 ; i<argc ; i++ )
    {
        for ( j=0 ; argv[i][j] ; j++ )
            buf_out[k++]=argv[i][j];
        buf_out[k++]=0;
    }
    return k;
}

int main(argc,argv)
    int                 argc;
    char                **argv;
{
    int                 sock;
    struct sockaddr_in  server;
    struct hostent      *hp, *gethostbyname();
    char                buf[BUFSIZE];
    FILE                *stream;
    int                 port=BIBLE_PORT;
    char                servername[256];
    int                 with=0;
    char                type='U';
    char                *s, c;
    int                 uidonly=FALSE;
    char                *tmp;
    int                 len;

    int i,j,k;
    
    progName = argv[0];
    
    /**
     ** Test to see if the envirment variable has been set to overide
     ** the server port. Of form hostname:portno
     **/
    
    tmp=getenv("BIBLE_SERVER");
    if (tmp)
    {
        /* pick out the port no */
        char            *p;

        strcpy(servername,tmp);
        for (p=servername; *p!=0; p++)
            if (*p==':')
                break;

        if (*p==0)
        {
            fprintf(stderr, "%s: Warning! Misformatted enviroment "
                    "variable BIBLE_SERVER\n", progName);
            strcpy(servername,BIBLE_SERVER);  
        }
        else
        {
            *p=0;
            port=atoi(p+1);
        }
    }
    else
    { 
        strcpy(servername,BIBLE_SERVER);  
    }
    
    /**
     ** Do the grungy socket stuff
     ** Care to deal with all errors 
     **/
    
    sock=socket(AF_INET,SOCK_STREAM,0);
    if (sock<0)
    {
        perror("socket");
        exit(1);
    }
    
    server.sin_family = AF_INET;
    
    hp=gethostbyname(servername);
    if (hp==0)
    {
        fprintf(stderr,"%s: unknown host\n", progName);
        exit(1);
    }

    memcpy((char*) & server.sin_addr,(char *)hp->h_addr,hp->h_length);
    
    server.sin_port = htons(port);
    
    /**
     ** Connect may hang if machine is alive but confused, 
     ** don't let it 
     **/
    
    signal (SIGALRM, connect_timed_out);
    alarm (CONNECT_TIME_OUT);
    
    if(connect(sock,(struct sockadr *)& server,sizeof(server))<0)
    {
        perror("connect");
        exit(1);
    }
    
    /**
     ** Just in case the other end crashes without closing
     ** and we hang on a write
     ** time out the whole program after a while 
     **/
    
    alarm (NORMAL_TIME_OUT);
    signal (SIGALRM, normal_time_out);

    out=stdout;                 /* Default to printing to stdout. */
    out_flag=0;
    
    k=parse_cmd_line( argc, argv );
    
    if (k<0)
    {
        fprintf(stderr,"%s: Error in parsing command "
                "line.\n",progName);
        exit(1);
    }
    
    write(sock,buf_out,k);
    
    read_data( sock );
    
    close(sock);

    if (out_flag)
        fclose(out);

    return 0;
}

