/*	
        DFTEST -- test "dfl," a dataflow interpreter
        (c) 1996, Howard E. Motteler, 

NAME:  dftest

USAGE: dftest [-s n] [-r n] [-o output sequence]

DESCRIPTION:

  The dftest program is used to test a "dfl" (dataflow interpreter)
  project.  Dftest reads an output sequence, either from stdin or
  as a command line argument if the "-o" flag is given.  The output
  sequence is specified as a list of <channel>=<value> pairs, e.g.,
  c1=2 c2=4 c3=99 c1=0 c1=2, etc.  The <value> can be either an
  integer or the special symbol "end".

  After reading an output sequence, dftest forks to become two
  processes: a sender that writes the specified values to the
  specified buffers (or channels) and a receiver that reads from
  buffer (or channel) zero.  The sender prints values just before
  they are sent, and the receiver prints values as soon as they
  are received.  When dftest's receiving process receives an END,
  or if the number of values received exceeds a fixed limit, it
  terminates.

OPTIONS:

   -o <seq>  use <seq> instead of stdin as output sequence
   -s <n>    sleep <n> seconds between sends; default is 1
   -r <n>    receive at most <n> values; default is number sent

BUGS:

  1. dftest only knows about channels c0-c9 
  2. dftest currently only receives on channel 0

*/

#include <stdio.h>
#include <stdlib.h>
#include <sys/signal.h>
#include "dftest.h"

/*  parameters and limits
 */

#define MAXLEN	100	/* longest allowable output sequence */
#define BUFLEN   20	/* temporary char buffer size */
#define INPCHAN   0
#define DEBUG     0	/* initial debug value */

int outval[MAXLEN];	/* array of values to be sent */
int outbuf[MAXLEN];	/* corresponding channels for values */
char buf[BUFLEN];	/* temporary char buffer */


/*   main program
 */
main (argc, argv) int argc; char *argv[]; {

    int i, j, k, c;
    int oflag=0, nout=0, debug=DEBUG;
    int recvlim=0, sendwait=1;
    char *p;
    int pid;

    extern int getopt();
    extern char *optarg;
    extern int   optind;
    extern char *sbase();

    /*  parse command line input 
     */
    while ((c=getopt(argc, argv, "ds:r:o:")) != EOF)
       switch (c) {

	case 'd':   

	  /*  set debug flag 
	   */
	  debug = 1;
	  break;

	case 's':   

	  /*  set wait between sends
	   */
	  sendwait = atoi(optarg);
	  break;

	case 'r':

	  /*  max number of values to receive on channel 0
	   */
	  recvlim = atoi(optarg);
	  break;

	case 'o':   

	  /*  read output commands from the remaining command line 
	   */
	  oflag = 1;
	  for (i=optind-1,nout=0; i<argc; i++,nout++)
	     if (argv[i][0] != 'c' || argv[i][2] != '=') {
		printf("%s: bad <chan>=<value> cmd: %s\n", argv[0], argv[i]);
		exit(2);
		}
	     else {
		outbuf[nout]  = argv[i][1]-'0';
		if (strncmp("end", argv[i]+3, 3) == 0)
		   outval[nout]  = END ;
		else
		   outval[nout]  = atoi(argv[i]+3) ;
		}
	  break;

	case '?':	

	  /*  command line error
	   */
	  printf("Usage: %s [-d] [-o output sequence]\n", argv[0]);
	  exit(2);
	  break;
	  }


    if (!oflag) {	

       /*  read output commands from stdin
	*/
       for (nout = 0, c = getchar(); c != EOF; ) {

	  i = 0;
	  p = buf; 

	  while (c != EOF && isspace(c)) 
	     c=getchar();

	  while (c != EOF && !isspace(c) && i<BUFLEN) {
	     i++; *p++ = c; c=getchar();
	     }

	  *p = '\0';
	  
	  if (i > 3 && buf[0] == 'c' && buf[2] == '=') {
	     outbuf[nout]  = buf[1] - '0';
	     if (strncmp("end", buf+3, 3) == 0)
		outval[nout]  = END ;
	     else
		outval[nout]  = atoi(buf+3) ;
	     nout++;
	     }
	  else if (i != 0) {
	     printf("%s: bad <chan>=<value> cmd: %s\n", argv[0], buf);
	     exit(2);
	     }
	  }
       }


    /*  semaphore and shared memory initialization
     */
    sinit();  /* initialize or attach to semaphore block */

    p = sbase();
    for (i=0; i < NBUFS*sizeof(bufrec); i++) *p++ = 0;
    for (i=0; i < NSEMS; i++) ssig(i);
    for (i=0; i < NBUFS-3; i++) ssig(READY);


    /*  become two processes: a sender and a receiver
     */
    if ((pid=fork()) != 0) {

       /*  we are the receiving process 
	*
	*  This process (the parent) receives vaues from the INPCHAN
	*  buffer until either END or recvlim values have been received.
	*  Then sclear() is called to deallocate semaphores and free the
	*  shared memory.
	*/

       if (recvlim == 0) recvlim = nout;

       for (i=0, j=0, k=0; k != END && i < recvlim; i++)

	  if ((k=readbuf(INPCHAN)) == END)
	     printf("%s: received end\n", argv[0]);

	  else {
	     printf("%s: received %d\n", argv[0], k);
	     j++;
	     }
	      

       /*  exit after clearing semaphores and shared memory 
	*/
       printf("%s: received %d values\n", argv[0], j);
       printf("%s: clearing semaphores\n", argv[0]);
       sclear();
       }

    else {

       /*  we are the sending process
	*
	*  This process (the child) process sends the test
	*  data, and terminates quietly when everything has
	*  been sent.
	*/

       for (i=0; i<nout; i++) {
	  if (outval[i] == END)
	     printf("%s: sending c%d=end\n", argv[0], outbuf[i]);
	  else
	     printf("%s: sending c%d=%d\n", argv[0], outbuf[i], outval[i]);
	  writebuf(outbuf[i], outval[i]);
	  if (sendwait > 0) sleep(sendwait);
	  }
       
	/*  Detach from shared memory, but don't delete shared 
	 *  memory or sem's (other processes may still be using
	 *  then) and then exit quietly.
	 */
	shmdetatch();

       }
  }
