/*	$Id$
 *	Author:	Roger A. Cole
 *	Date:	11-26-90
 *
 *	Experimental Physics and Industrial Control System (EPICS)
 *
 *	Copyright 1991, the Regents of the University of California,
 *	and the University of Chicago Board of Governors.
 *
 *	This software was produced under  U.S. Government contracts:
 *	(W-7405-ENG-36) at the Los Alamos National Laboratory,
 *	and (W-31-109-ENG-38) at Argonne National Laboratory.
 *
 *	Initial development by:
 *		The Controls and Automation Group (AT-8)
 *		Ground Test Accelerator
 *		Accelerator Technology Division
 *		Los Alamos National Laboratory
 *
 *	Co-developed with
 *		The Controls and Computing Group
 *		Accelerator Systems Division
 *		Advanced Photon Source
 *		Argonne National Laboratory
 *
 * Modification Log:
 * -----------------
 * .01	11-26-90	rac	initial version
 * .02	07-30-91	rac	installed in SCCS
 * .03  09-11-91	joh	updated for v5 vxWorks
 * .03	11-07-91	rac	be more forgiving of unexpected interactions
 * .04	12-06-91	rac	total rewrite, Unix-only, no LWP
 *
 * make options
 *	-DNDEBUG	don't compile assert() checking
 *      -DDEBUG         compile various debug code, including checks on
 *                      malloc'd memory
 */
/*+/mod***********************************************************************
* TITLE	cmdClient.c - general purpose client for command-based servers
*
* DESCRIPTION
*	Connects to a text-command-based server.
*
*	Usage on SunOS:
*		% cmdClient hostName portNum
*	    or
*		execl("cmdClient", "cmdClient", "hostName", "portNum",
*								(char *)0);
*
*	Usage on VxWorks:
*		> cmdClient "hostName",portNum
*
* BUGS
* o	the stdout stream from this program contains, intermixed, the
*	server's stdout and stderr streams
* o	not all signals are caught
*-***************************************************************************/
#include <genDefs.h>
#include <tsDefs.h>
#include <ezsSockSubr.h>

#include <stdio.h>
#include <ctype.h>
#include <strings.h>
#include <signal.h>
#include <setjmp.h>
#include <sys/types.h>
#include <sys/time.h>

#include <sys/resource.h>



/*-----------------------------------------------------------------------------
* prototypes
*----------------------------------------------------------------------------*/
int cmdcl();
void cmdclCmdProcess();
char *cmdclInTask();
long cmdclTask();
void cmdclTaskSigHandler();

/*-----------------------------------------------------------------------------
* global definitions
*----------------------------------------------------------------------------*/

jmp_buf	sigEnv;		/* environment for longjmp at signal time */

main(argc, argv)
int     argc;           /* number of command line args */
char   *argv[];         /* command line args */
{
    char	*hostName;	/* host name for server */
    int		portNum;	/* port number for server */

    if (argc != 3)		/* must be command and 2 args */
	goto mainUsage;
    hostName = argv[1];
    if (sscanf(argv[2], "%d", &portNum) != 1) {
	printf("error scanning port number\n");
	goto mainUsage;
    }

    return cmdClient(hostName, portNum);

mainUsage:
    printf("Usage: %s serverHostName serverPortNumber\n", argv[0]);
    return -1;
}

/*+/subr**********************************************************************
* NAME	cmdClient - shell callable interface for cmdClient
*
*-*/
int
cmdClient(hostName, portNum)
char	*hostName;	/* I host name for server */
int	portNum;	/* I port number for server */
{
    long	stat;		/* status return from calls */
    int		serverSock=-1;	/* socket connected to server */
    char	serverBuf[80];
    char	*pBuf;
    FILE	*myIn=NULL;
    FILE	*myOut=NULL;
    char	*findNl;
    char	message[80];
    int		i;
    char	keyboard[80];	/* line from keyboard */
    int		eofFlag=0;
    int		discardNL=0;

    genSigInit(cmdclTaskSigHandler);

    if (setjmp(sigEnv) != 0)
	goto cmdclTaskWrapup;

/*-----------------------------------------------------------------------------
*    attempt to connect to the server.  If the connection is successful,
*    print the message returned by the server.  Then open two streams to
*    the socket, one for characters from keyboard to server, the other
*    for characters from server to stdout.
*----------------------------------------------------------------------------*/
    if ((i=ezsConnectToServer(&serverSock, portNum, hostName, serverBuf)) < 0) {
	if (i == -1) {
	    (void)sprintf(message, "connect to %s port:%d", hostName, portNum);
	    perror(message);
	}
	else
	    printf("%s\n", serverBuf);
	serverSock = -1;
	goto cmdclTaskWrapup;
    }
    printf("%s\n", serverBuf);
    if (ezsFopenToFd(&myIn, &serverSock) < 0) {
	perror("myIn");
	myIn = NULL;
	goto cmdclTaskWrapup;
    }
    setbuf(myIn, NULL);
    if (ezsFopenToFd(&myOut, &serverSock) < 0) {
	perror("myOut");
	myOut = NULL;
	goto cmdclTaskWrapup;
    }

/*----------------------------------------------------------------------------
*    "processing loop"
*	do the interactions with the server, attempting as much as possible
*	to show the messages as they come in from the server.
*---------------------------------------------------------------------------*/
    while (!eofFlag) {
        if (cmdclInTask(keyboard) != NULL) {
	    fputs(keyboard, myOut);
	    fflush(myOut);
	}

	if (ezsCheckFpRead(myIn)) {
	    int		i, c;

	    i = 0;
	    while (ezsCheckFpRead(myIn)) {
		if ((c = fgetc(myIn)) == EOF) {
		    eofFlag++;
		    break;
		}
		serverBuf[i++] = c;
		if (i >= 79 || c == '\n')
		    break;
	    }
	    pBuf = serverBuf;
	    serverBuf[i] = '\0';
	    if (i > 0) {
		char	*findChr;
		if ((findChr = index(pBuf, '#')) != NULL) {
		    while (pBuf != findChr) {
			putchar(*pBuf);
			pBuf++;
		    }
		    if (strncmp(pBuf, "#p#r#", 5) == 0) {
			pBuf += 5;
			discardNL = 1;
		    }
		}
		else
		    pBuf = serverBuf;
		if (discardNL) {
		    if ((findNl = index(pBuf, '\n')) != NULL) {
			*findNl = '\0';
			discardNL = 0;
		    }
		}
		fputs(pBuf, stdout);
		fflush(stdout);
	    }
	    else {
		if (strncmp(keyboard, "quit", 4) == 0)
		    break;
		else if (strncmp(keyboard, "close", 5) == 0)
		    break;
		else if (strncmp(keyboard, "disconnect", 10) == 0)
		    break;
		else
		    printf("null message from server\n");
	    }
	}
	sleep(1);
    }

cmdclTaskWrapup:
    if (myIn != NULL)
	fclose(myIn);
    if (myOut != NULL)
	fclose(myOut);
    if (serverSock >= 0)
	close(serverSock);

    return 0;
}

/*+/subr**********************************************************************
* NAME	cmdclTaskSig - signal handling and initialization
*
* DESCRIPTION
*	These routines set up for the signals to be caught for cmdclTask
*	and handle the signals when they occur.
*
* RETURNS
*	void
*
* BUGS
* o	not all signals are caught
* o	it's not clear how useful it is to catch the signals which originate
*	from the keyboard
*
*-*/
void
cmdclTaskSigHandler(signo)
int	signo;
{
    printf("entered cmdclTaskSigHandler for signal:%d\n", signo);
    signal(signo, SIG_DFL);
    longjmp(sigEnv, 1);
}

/*+/subr**********************************************************************
* NAME	cmdclInTask - handle keyboard input
*
* DESCRIPTION
*	Checks to see if input is available and reads it, if so.  If
*	there is EOF, then question is asked whether to shut down the
*	server.
*
* RETURNS
*	char *, or
*	NULL
*
*-*/
char *
cmdclInTask(buf)
char	*buf;
{
    char	*input;
    char	message[80];
    fd_set      fdSet;          /* set of fd's to watch with select */
    int         fdSetWidth;     /* width of select bit mask */
    struct timeval fdSetTimeout;/* timeout interval for select */

    struct rlimit rlp;

/* MDA - replace obsolete system call
    fdSetWidth = getdtablesize();
*/
    getrlimit(RLIMIT_NOFILE,&rlp);
    fdSetWidth = rlp.rlim_cur;

    fdSetTimeout.tv_sec = 0;
    fdSetTimeout.tv_usec = 0;
    FD_ZERO(&fdSet);
    FD_SET(fileno(stdin), &fdSet);

    if (select(fdSetWidth, &fdSet, NULL, NULL, &fdSetTimeout) == 0)
	return NULL;
    if (fgets(buf, 80, stdin) == NULL) {
	strcpy(buf, "quit\n");
	clearerr(stdin);
    }
    if (strncmp(buf, "quit", 4) == 0) {
	(void)printf("stop the server (y/n) ? ");
	if (fgets(message, 80, stdin) == NULL) {
	    strcpy(message, "y");
	}
	if (message[0] == 'y' || message[0] == 'Y')
	    strcpy(buf, "quit\n");
	else
	    strcpy(buf, "\n");
    }

    return buf;
}
