#ifndef lint
static char *RCSid = "$Id: tracing.c,v 1.9 1993/05/10 05:49:42 anders Exp anders $";
#endif

/*
 *  The Regina Rexx Interpreter
 *  Copyright (C) 1992-1994  Anders Christensen <anders@pvv.unit.no>
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public
 *  License along with this library; if not, write to the Free
 *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/*
    This code modified for Win32 port by Ataman Software, Inc. June 29, 1995.
*/

#include "rexx.h"
#include <stdio.h>
#if !defined(WIN32)
#include <unistd.h>
#endif
#include <ctype.h>
#include <assert.h>
#include <string.h>

static int traceflag = 0 ;
static int lasttracedline = -1 ;   
static int ctrlcounter = 0 ;
static int notnow = 0 ;

static char tracestr[LINELENGTH+1] ;


extern sysinfo systeminfo ;

int pushcallstack( treenode *this )
{
   nodeptr *tmpptr ; 
   extern sysinfo systeminfo ;
   
   if (systeminfo->cstackcnt >= systeminfo->cstackmax)
   {
      assert( systeminfo->cstackcnt == systeminfo->cstackmax ) ;
      tmpptr = Malloc( (systeminfo->cstackmax*2+10)*sizeof(nodeptr)) ;
      if (systeminfo->callstack)
      {
         memcpy( tmpptr, systeminfo->callstack, 
                                 systeminfo->cstackcnt*sizeof(nodeptr) ) ;
         Free( systeminfo->callstack ) ;
      }
      systeminfo->callstack = tmpptr ;
      systeminfo->cstackmax *= 2 ;
      systeminfo->cstackmax += 10 ;
   }

   ctrlcounter++ ;      
   systeminfo->callstack[systeminfo->cstackcnt++] = this ;
   return systeminfo->cstackcnt ;
}


void popcallstack( int value )
{
   extern sysinfo systeminfo ;

   if (value>=0)
   {
      assert( systeminfo->cstackcnt >= value ) ;
      ctrlcounter -= systeminfo->cstackcnt - value ;
      systeminfo->cstackcnt = value ;
   }

   ctrlcounter-- ;
   --(systeminfo->cstackcnt) ;

   assert( systeminfo->cstackcnt >= 0 ) ;
   assert( ctrlcounter >= 0 ) ;
}


void printout( streng *message ) 
{
   int rc ;

   rc = HOOK_GO_ON ;
   if (systeminfo->hooks & HOOK_MASK(HOOK_STDERR))
      rc = hookup( HOOK_STDERR, message ) ;

   if (rc==HOOK_GO_ON)
   {
      write( fileno(stderr), message->value, message->len ) ;
      putc( 0x0a, stderr ) ;
   }
}




void traceerror( treenode *this, int RC )
{
   streng *message ;

   if (trace_stat=='N') 
      traceline(this, 'C', 0) ; 

   if (trace_stat!='O')
   {
      message = Str_make( 20+sizeof(int)*3 ) ;
      sprintf(message->value, "       +++ RC=%d +++",RC) ; 
      message->len = strlen( message->value ) ;

      printout( message ) ;
      Free_string( message ) ;
   }
}


void tracecompound( streng *stem, int length, streng *index, char trch )
{
   streng *message ;
  
   if ((traceflag)||(trace_stat!='I'))
      return ;
   
   stem = Str_ify(Str_dup(stem)) ;
   index = Str_ify(Str_dup(index)) ;
   message = Str_make( stem->len + index->len + 30 + ctrlcounter ) ;

   sprintf(tracestr,"       >%c> %%%ds  \"%%.%ds.%%s\"",trch,
                                                      ctrlcounter,length) ;
   sprintf(message->value, tracestr,"",stem->value,index->value ) ; 

   message->len = strlen( message->value ) ;
   printout( message ) ;

   Free_string( message ) ;
   Free( stem ) ;
   Free( index ) ;
}


void starttrace( void ) 
{
   notnow = 1 ;
}

void tracemsg( void )
{
   streng *message ;
   int rc ;
   char *msg= "       +++ Interactive trace. TRACE OFF to end debug, ENTER to continue. +++" ;

   rc = HOOK_GO_ON ;
   message = Str_cre( msg ) ;
   printout( message ) ;
   Free_string( message ) ;
}


int intertrace( void )
{
   streng *str ;
   int retvalue, rc ;
  
   if (traceflag)
      return 0 ;

   if (notnow==1) 
   {
      notnow = 2 ;
      return 0 ; 
   }
   else if (notnow==2) 
   {
      notnow = 0 ;
      tracemsg() ; 
   }

   traceflag = 1 ;
   retvalue = (-1) ;
   for (; retvalue<0; ) {
      rc = HOOK_GO_ON ;
      if (systeminfo->hooks & HOOK_MASK(HOOK_TRCIN))
         rc = hookup( HOOK_TRCIN, &str ) ;

      if (rc==HOOK_GO_ON)
         str = readkbdline() ;

      if (str->len==0) 
      {
         traceflag = 0 ;
         retvalue = 0 ;
      }

      if ((Str_len(str)==1)&&(str->value[0]=='='))
      {
         traceflag = 0 ;
         retvalue = 1 ;
      }
      else if (str->len)
      {
         dointerpret( str ) ; 
         if (!systeminfo->interactive)
            return 0 ;
      }
   }

   return retvalue ;
}


void tracenumber( num_descr *num, char type ) 
{
   extern proclevel currlevel ;
   char tmpch ;
   streng *message, *tmpstr ;
   
   if (traceflag)
      return ;

   tmpch = currlevel->tracestat ;
   if ((tmpch=='I')||((tmpch=='R')&&(type!='.')))
   {
      tmpstr = str_norm( num, NULL ) ;
      message = Str_make( 30 + ctrlcounter + tmpstr->len ) ;
      sprintf(tracestr,"       >%%c> %%%ds  \"%%s\"",ctrlcounter) ;
      sprintf(message->value,tracestr,type,"", Str_ify(tmpstr)->value) ; 
      message->len = strlen( message->value ) ;
      printout( message ) ;
      Free_string( message ) ;
      Free_string( tmpstr ) ;
   }
}


void tracebool( int value, char type ) 
{
   char tmpch ;
   extern proclevel currlevel ;
   streng *message ;

   if (traceflag)
      return ;

   tmpch = currlevel->tracestat ;
   if ((tmpch=='I') || ((tmpch=='R') && (type!='.')))
   {
      message = Str_make( 35 + ctrlcounter ) ;
      sprintf(tracestr,"       >%%c> %%%ds  \"%%d\"",ctrlcounter ) ;
      sprintf(message->value,tracestr,type,"",value) ;
      message->len = strlen( message->value ) ;
      printout( message ) ;
      Free_string( message ) ;
   }
}


void tracevalue( streng *str, char type )
{
   extern proclevel currlevel ;
/*   extern char tracestr[] ; */
   char tmpch ;
   streng *message ;

   if (traceflag)
      return ;

   tmpch = currlevel->tracestat ;
   if ((tmpch=='I')||((tmpch=='R')&&(type!='.'))) 
   {
      message = Str_make( str->len + 30 + ctrlcounter ) ;
      sprintf(tracestr,"       >%%c> %%%ds  \"%%s\"",ctrlcounter) ;
      sprintf(message->value,tracestr,type,"", Str_ify(str)->value) ; 
      message->len = strlen( message->value ) ;
      printout( message ) ;
      Free_string( message ) ;
   }
}



void traceline( treenode *this, char tch, int offset ) 
{
   streng *srcstr ;
   extern sysinfo systeminfo ;
   
   if ((tch=='O')||(traceflag))
      return ; 

   if ((this->charnr<0)||(this->lineno<0)) 
      return ;

   if ((tch=='I')||(tch=='R')||(tch=='A')) goto trace ;
   if ((tch=='L')&&(this->type==X_LABEL)) goto trace ;
   if (tch=='C')
      if ((this->type==X_COMMAND) || 
          ((this->type==X_ADDR_N) && (this->p[0])))
         goto trace ;

   return ;

trace: {
       streng *message ;
       srcstr = getsourceline(this->lineno,this->charnr,
                      systeminfo->firstline, systeminfo->lastline) ; 

       message = Str_make( ctrlcounter+20+srcstr->len+offset ) ;
       if (this->lineno==lasttracedline) {
          sprintf(tracestr,"       *-* %%%ds%%s",ctrlcounter+offset) ;
          sprintf(message->value,tracestr,"", Str_ify(srcstr)->value) ; }
       else {
          sprintf(tracestr,"%%6d *-* %%%ds%%s",ctrlcounter+offset) ;
          sprintf(message->value,tracestr,this->lineno,"", 
                                          Str_ify(srcstr)->value) ; }

       message->len = strlen( message->value ) ;
       printout( message ) ;
       lasttracedline = this->lineno ;
       Free_string( message ) ;
       Free(srcstr) ; }
}

/*
char *formatstring( int level, int lineno, char around, char middle ) 
{
   char *ptr ;

   if (lineno>0)
   {
      *(ptr++)='%' ;
      *(ptr++)='6' ;
      *(ptr++)='d' ;
   }
   else
   {
      memset( ptr, ' ', 6 ) ;
      ptr += 6 ;
   }

   *(ptr++) = ' ' ;
   *(ptr++) = around ;
   *(ptr++) = middle ;
   *(ptr++) = around ;
   *(ptr++) = ' ' ;
      
   if (level>12 && currlevel->options.prune_trace)
   {
      memcpy( ptr, "[...] ", 6 ) ;
      ptr += 6 ;
      memset( ptr, ' ', 30 ) ;
      ptr += 30 ;
   }
   else
   {
      memset( ptr, '
   }


      sprintf(fmt,"%%6d %c%c%c [...] %%%ds%%s", around, middle, around, 30 ) ;
   else
      sprintf(fmt,"%%6d %c%c%c %%%ds%%s", around, middle, around, level*3 ) ;
}
*/



void traceback( void )
{
   extern nodeptr currentnode /*, callstack[] */ ;
   static char fmt[20] ;
   extern sysinfo systeminfo ;
   sysinfo ss ;
   streng *message ;
   nodeptr ptr ;
   int i, j ;

   message = Str_make( 512 + ctrlcounter ) ;
   if (currentnode)
   {
      sprintf(fmt,"%%6d +++ %%%ds%%s", ctrlcounter*3 ) ;
      sprintf(message->value,fmt,currentnode->lineno,"",
         Str_ify(getsourceline(currentnode->lineno,
                   currentnode->charnr, systeminfo->firstline,
                   systeminfo->lastline))->value) ;
      message->len = strlen( message->value ) ;
      printout( message ) ;
   }
   
   j = ctrlcounter ;
   for (ss=systeminfo; ss; ss=ss->previous ) 
   {
      for (i=ss->cstackcnt-1;i>=0;i--) 
      {
         ptr = ss->callstack[i] ;
         if (!ptr)
            continue ;
         if (--j>12 && currlevel->u.options.prune_trace)
            sprintf(fmt,"%%6d +++ [...] %%%ds%%s", 30 ) ;
         else
            sprintf(fmt,"%%6d +++ %%%ds%%s", (j)*3 ) ;

         sprintf(message->value,fmt,ptr->lineno,"",
            Str_ify(getsourceline(ptr->lineno,ptr->charnr,
                                  ss->firstline, ss->lastline))->value) ; 

         message->len = strlen( message->value ) ;
         printout( message ) ;
      } 
   }
   Free_string( message ) ;
}


static char buf[32] ;
static int bufptr=0 ;

void queue_trace_char( char ch )
{
   if (bufptr<32)
      buf[bufptr++]=ch ;
   else
      exiterror( ERR_INTERPRETER_FAILURE ) ;
}


void flush_trace_chars( void )
{
   int cnt ;

   for (cnt=0; cnt<bufptr; cnt++)
      set_trace_char( buf[cnt] ) ;

   bufptr = 0 ;
}


void set_trace_char( char ch )
{
   extern sysinfo systeminfo ;
   extern proclevel currlevel ;
 
   switch (ch=toupper(ch))
   {
      case '?':
         ch = systeminfo->interactive = !systeminfo->interactive ;
         if (ch)
            starttrace() ;
         break ;         

      case 'F':
         ch = 'N' ;

      case 'E':
      case 'A':
      case 'C':
      case 'I':
      case 'L':
      case 'N':
      case 'O':
      case 'R':
      case 'S':
         currlevel->tracestat=ch ;
         break ;

      default:
         exiterror( ERR_INVALID_TRACE ) ;
   }

   if (ch=='O')
      systeminfo->interactive = 0 ;

   trace_stat = currlevel->tracestat ;
}



void set_trace( streng *setting )
{
   int cptr ;

   if (myisnumber(setting))
      if (myiswnumber(setting))
         return ;  /* need to do something else ... */
      else
         exiterror( ERR_INVALID_INTEGER ) ;

   for (cptr=0; cptr<Str_len(setting); cptr++)
   {
      set_trace_char( setting->value[cptr] ) ;
      if (isalpha( setting->value[cptr])) 
         return ;
   }
}


