expect.c 5.28 KB
/*
 * Copyright (C) 1996-1998 by the Board of Trustees
 *    of Leland Stanford Junior University.
 * 
 * This file is part of the SimOS distribution. 
 * See LICENSE file for terms of the license. 
 *
 */


/*****************************************************************
 * expect.c
 *
 * Expect scripting facility for SimOS
 *
 ****************************************************************/



#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <math.h>
#include <setjmp.h>
#include <ctype.h>
#include <assert.h>

#include "syslimits.h"
#include "simutil.h"
#include "sim_error.h"
#include "machine_params.h"
#include "memstat.h"
#include "annotations.h"
#include "cpu_interface.h"
#include "symbols.h"
#include "tcl_init.h"
#include "hw_events.h"
#include "visual.h"
#include "checkpoint.h"

#define MAX_OUT_STRING 8192 /* max. size of output string to compare */

typedef struct ConInfo ConInfo;
typedef struct Trigger Trigger;

struct ConInfo {
   char out[MAX_OUT_STRING+1];
   int  outindx;
   char in[MAX_OUT_STRING];
   int  front;
   int  back;
};

struct Trigger {
   Tcl_Interp *interp;
   int        console;
   char       *pattern;
   char       *script;
   Trigger    *next;
};


static ConInfo *ci;
static Trigger *triggers;

static int ExpectCmd(ClientData data, Tcl_Interp *interp, int argc, char *argv[]);
static int TypeCmd(ClientData data, Tcl_Interp *interp, int argc, char *argv[]);


void ExpectInit(Tcl_Interp *interp)
{
   int i;

   ASSERT(SIM_MAXCPUS);
   ci = (ConInfo *) malloc(sizeof(ConInfo) * SIM_MAXCPUS);
   bzero((char*)ci,sizeof(ConInfo) * SIM_MAXCPUS);

   Tcl_CreateCommand(interp, "expect", ExpectCmd,
                     (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
   
   Tcl_CreateCommand(interp, "type", TypeCmd,
                     (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
   
   triggers = NULL;

   for (i=0; i<SIM_MAXCPUS; i++) {
      ci[i].outindx = 0;
      ci[i].out[0] = '\0';
      ci[i].front = 0;
      ci[i].back = 0;
      ci[i].in[0] = '\0';
   }
}

int ExpectCmd(ClientData data, Tcl_Interp *interp, int argc, char *argv[])
{
   char *pattern;
   int console;
   char *script;
   Trigger *newtrig;
   
   if (argc == 3) {
      console = -1;
      pattern = argv[1];
      script = argv[2];
      
   } else if (argc == 4) {
      pattern = argv[2];
      script = argv[3];
      
      if (Tcl_GetInt(interp, argv[1], &console) != TCL_OK) {
         Tcl_AppendResult(interp, "consolenum must an int", NULL);
         return TCL_ERROR;
      }
      
      if (console < 0 ) {
         Tcl_AppendResult(interp, "bad consolenum \"", argv[1], "\"", NULL);
         return TCL_ERROR;
      }
      
   } else {
      Tcl_AppendResult(interp, "wrong # args: should be \"",
                       argv[0], " ?consolenum? pattern script\"", NULL);
      return TCL_ERROR;
   }

   newtrig = (Trigger*)MALLOC(sizeof(Trigger), "Trigger");
   newtrig->interp = interp;
   newtrig->console = console;
   newtrig->pattern = SaveString(pattern);
   newtrig->script = SaveString(script);
   newtrig->next = triggers;
   triggers = newtrig;

   return TCL_OK;
}

extern bool sim_console_has_char(int);

int TypeCmd(ClientData data, Tcl_Interp *interp, int argc, char *argv[])
{
   char *string;
   int console;
   
   if (argc == 2) {
      string = argv[1];
      console = 0;
      
   } else if (argc == 3) {
      string = argv[2];

      if (Tcl_GetInt(interp, argv[1], &console) != TCL_OK) {
         Tcl_AppendResult(interp, "consolenum must an int", NULL);
         return TCL_ERROR;
      }
      
      ASSERT(TOTAL_CONSOLES);
      if ((console < 0 ) || (console >= TOTAL_CONSOLES)) {
         Tcl_AppendResult(interp, "bad consolenum \"", argv[1], "\"", NULL);
         return TCL_ERROR;
      }
      
   } else {
      Tcl_AppendResult(interp, "wrong # args: should be \"",
                       argv[0], " ?consolenum? string\"", NULL);
      return TCL_ERROR;
   }

#ifndef SIM_X86
   sim_console_has_char(console);
#endif
   
   while (*string) {
      if (ci[console].back == (ci[console].front-1)) {
         Tcl_AppendResult(interp, "overflowed internal buffer", NULL);
         return TCL_ERROR;
      }
      ci[console].in[ci[console].back] = *string;
      ci[console].back = (ci[console].back + 1) % MAX_OUT_STRING;
      string++;
   }

#ifdef SIM_X86
   sim_console_has_char(console);
#endif

   return TCL_OK;
}

char ExpectGetChar(int consoleNum)
{
   if (ci[consoleNum].back == ci[consoleNum].front) {
      return '\0';
   }

   return ci[consoleNum].in[ci[consoleNum].front++];
}

void ExpectPutChar(int consoleNum, char ch)
{
   Trigger *trig = triggers;

   if (ci[consoleNum].outindx < MAX_OUT_STRING) {
      ci[consoleNum].out[ci[consoleNum].outindx++] = ch;
      ci[consoleNum].out[ci[consoleNum].outindx] = '\0';

      while (trig) {
         if ((trig->console == -1) || (trig->console == consoleNum)) {
            if (Tcl_StringMatch(ci[consoleNum].out, trig->pattern)) {
               if (Tcl_GlobalEval(trig->interp, trig->script) != TCL_OK) {
                  HandleError(trig->interp, trig->script);
               }
            }
         }
      
         trig = trig->next;
      }
   }
 
   if ((ch == '\n') || (ch == '\r')) {
      ci[consoleNum].out[0] = '\0';
      ci[consoleNum].outindx = 0;
      
   }
}