/* 

ASCII WM framegrabber 

gets all the nice frames and puts them in a file you specify

author: thomas peterseil, stb@sil.at

license: no rights, no obligations, just for free

*/

#include <sys/types.h>
#include <stdio.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <netdb.h>
#include <sys/time.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <ctype.h>
#include <strings.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>

#define BUFFERLENGTH    8192

#define FRAMESEP    "\x1B\x5B\x48"

int                 srcSock, fdFile;

struct s_buffer {
   char buf[BUFFERLENGTH];
   int len;
   int pos;
};

struct s_buffer           buffer;

#define ERRPRINT(format...) fprintf(stderr, format)

void closeSock() {

   if (srcSock != -1) {
      if (close(srcSock) == -1) {
         ERRPRINT("close failed: %m");
      }
      srcSock = -1;
   }
}

void closeFile() {

   if (fdFile != -1) {
      if (close(fdFile) == -1) {
         ERRPRINT("close failed: %m");
      }
      fdFile = -1;
   }
}


void signalHandler(int sig) {

    switch(sig) {
    
        case SIGPIPE:
            ERRPRINT("SIGPIPE\n");
            break;
        case SIGINT:
            ERRPRINT("closing handlers...\n");
            closeSock();
            closeFile();
            ERRPRINT("done.\n");          
            exit(0);
            break;
    } 
}

int readConnect (char *hostname, int port) {

    int                 rc;            /* system calls return value storage */
    int                 s;             /* socket descriptor                 */
    struct sockaddr_in  sa;            /* Internet address struct           */
    struct hostent*     hen;           /* host-to-IP translation            */

    hen = gethostbyname(hostname);
    if (!hen) {
        ERRPRINT("couldn't resolve host name: %m\n");
	return -1;
    }

    memset(&sa, 0, sizeof(sa));

    sa.sin_family = AF_INET;
    sa.sin_port = htons(port);
    memcpy(&sa.sin_addr.s_addr, hen->h_addr_list[0], hen->h_length);

    s = socket(AF_INET, SOCK_STREAM, 0);
    if (s < 0) {
        ERRPRINT("socket failed: %m\n");
	return -1;
    }

    rc = connect(s, (struct sockaddr *)&sa, sizeof(sa));

    if (rc != 0) {
	ERRPRINT("connect failed: %m\n");
        return -1;
    } else {
        return s;
    } 
}

#define usage() ERRPRINT("Usage: %s hostname port (filename | -) [RECONNECT | NORECONNECT]\n", argv[0])

int main(int argc, char **argv) {
    int rc;
    char *sp;
    int autoretry = 0;
    char *srcHost;
    char *filename;
    int srcPort;
    
    // init some vars
    
    buffer.len = BUFFERLENGTH;
    buffer.pos = 0;
    srcSock = -1;
    fdFile = -1;

    if ((argc != 4) && (argc != 5)) {
       usage();
       exit(2);
    }

    srcHost = argv[1];
    srcPort = atoi(argv[2]);
    filename = argv[3];
    if ((argc == 5) && (!strncmp("RECONNECT", argv[4], 9)))
       autoretry = 1;
    
    signal(SIGINT, signalHandler);
    
    while(1) {
        if (srcSock == -1) {           // need to connect
	   ERRPRINT("connecting to %s %i ...\n", srcHost, srcPort);
	   srcSock = readConnect(srcHost, srcPort);
	   if (srcSock == -1) {
	      if (autoretry) {
	         sleep(10);
	         continue;
	      } else {
		 exit(1);
	      }
	   }
           ERRPRINT("connection established\n");
	}    
	if ((fdFile == -1) && (strncmp("-", filename, 1))) {
	   ERRPRINT("opening file %s\n", filename);
	   fdFile = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666);
	   if (fdFile == -1) {
	      ERRPRINT("open failed: %m\n");
	      exit(2);
	   }
	}
//      ERRPRINT("buffer.pos = %i\n", buffer.pos);
        rc = read(srcSock, buffer.buf + buffer.pos, buffer.len - buffer.pos - 1);
        if (rc > 0) {
            buffer.pos += rc;
            buffer.buf[buffer.pos] = 0;
            // check on sep
            while (buffer.pos > 3) {
	           sp = strstr(buffer.buf + strlen(FRAMESEP), FRAMESEP);
	           if (sp != NULL) {
                   if (!strncmp("-", filename, 1)) { // output to stdout
		      write (1, buffer.buf, sp - buffer.buf);
		   } else {
                      lseek(fdFile, 0, SEEK_SET);
                      write(fdFile, buffer.buf, sp - buffer.buf);
                      // do we need fdatasync ??? is think so...
	              fdatasync(fdFile);
		   }

//	            ERRPRINT("seppos = %i, buffer.pos = %i\n", sp - buffer.buf, buffer.pos);
                    memmove(buffer.buf, sp, buffer.pos - (sp - buffer.buf));
                    buffer.pos -= sp - buffer.buf;
                    buffer.buf[buffer.pos] = 0;
	           } else {
                    break;
	           }
            }
            if (buffer.pos > (buffer.len - 1)) {
                ERRPRINT("buffer filled up and no sep, clearing buf\n");
                buffer.pos = 0;
            }
        } else {
            if (rc == 0) {
                ERRPRINT("read got end of file\n");
                // end of file what to do? maybe server closed?
		// for now we close the sock and try to reconnect
                if (autoretry) {
	           sleep(1);
		   closeSock();
		} else {
		   exit(1);
		}
            } else {
            // here we got an error 
                ERRPRINT("read failed %m\n");
		if (rc == EINTR) 
		   continue;
		// all other errors may relate to the socket connection
                if (autoretry) {
	           sleep(10);
		   closeSock();
		} else {
		   exit(1);
		}
            }
        }
    }

    exit(0);
}
