 /**[txh]********************************************************************
                                                                                
  Copyright (c) 2005 Juan Pablo D. Borgna <jpborgna en inti gov ar>
  Copyright (c) 2006-2007 Salvador E. Tropea <salvador en inti gov ar>
  Copyright (c) 2005-2007 Instituto Nacional de Tecnolog�a Industrial
  Copyright (c) 2012 Timothy Pearson <kb9vqf@pearsoncomputing.net>
  
 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; version 2.

 This program 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 General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 02111-1307, USA

  Description: Commands parser and executor.
                                                                                
***************************************************************************/    
/*****************************************************************************  
                                                                                
 Target:      Any                                                               
 Language:    C
 Compiler:    gcc 3.3.5 (Debian GNU/Linux)
 Text editor: SETEdit 0.5.5                                                     
                                                                                
*****************************************************************************/  

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "global.h"
#include "commands.h"
#include "bitshandle.h"

#define MAX_WIDTH 246

long
value_from(char *varname)
{
 if (!strncasecmp("BSIZEB2",varname,7))
   return strtol(bsize,NULL,0)/4;
 if (!strncasecmp("BSIZEB",varname,6))
   return strtol(bsize,NULL,0)/8;
 if (!strncasecmp("BSIZE",varname,5))
   return strtol(bsize,NULL,0);
 if (!strncasecmp("MSIZE",varname,5))
   return msize;
 return strtol(varname,NULL,0);
}

void
fill(FILE *fp, long c, long t, int *col)
{
 char b[4]="ZZ";
 sprintf(b,"%02x",(unsigned int)c);
 int co=*col;

 for (;t>0;t--)
    {
     co+=fprintf(fp,"%s",b);
     if (co>=MAX_WIDTH)
       {
        co=0;
        fprintf(fp,"\n");
       }
    }
 *col=co;
}

/* This function executes a $COMMAND$ */
int
do_command(char *cmd, FILE *dfp, FILE *ofp, int *col)
{
 char *temp;
 char *dbuffer;
 long nbytes=0l;
 long readed=0l;
 long value=0l;
 long times=0l;
 int padded=0;
 char *args_pos_s=NULL;
 char *args_pos_e=NULL;
 char *separator=NULL;
 char args[100]; /* I dont think i need more than 100 bytes for
                    passing args... */

 /* DATA and DATA_INV command */
 
 if (strstr(cmd,"DATA"))
    {
     /* get the args inside the () */
     args_pos_s=strchr(cmd,'(');
     args_pos_e=strchr(cmd,')');
     if( !args_pos_s || !args_pos_e)
         {
          fprintf(stderr,"Bad argument specification in $DATA_INV(ndatabytes)$\n");
          return 1;
         }
      memcpy(args,args_pos_s+1,(size_t)(args_pos_e-args_pos_s)); /* Copy te command */
      args[args_pos_e-args_pos_s-1]='\0'; /* Put the null terminator */
     if ((nbytes=value_from(args))==0) /* Bad specification of number of bytes */
       {
        fprintf(stderr,"Bad specification of ndatabytes in $DATA_INV(ndatabytes)$\n");
        return 2;
       }
     if ((nbytes=value_from(args))==-1) /* Want to process the whole stream */
       {
        nbytes=s_bytes;
       }
     if ((temp=(char *)malloc(nbytes*2+1))==NULL)
       {
        fprintf(stderr,"Cannot asign %li bytes in memory needed for $DATA_INV(ndatabytes)$\n",nbytes*2+1);
        return 3;
       }
     if ((dbuffer=(char *)malloc(nbytes))==NULL)
       {
        free(temp);
        fprintf(stderr,"Cannot asign %li bytes in memory needed for $DATA_INV(ndatabytes)$\n",nbytes);
        return 3;
       }
      if ((readed=fread(dbuffer,1,nbytes,dfp))!=nbytes)
        {
         if (padded)
           {
            free(dbuffer);
            free(temp);
            fprintf(stderr,"Cannot read %li databytes for $DATA_INV(ndatabytes)$ from bitfile\n",nbytes);
            return 3;
           }
          padded=1;
          memset(dbuffer+readed,0xff,nbytes-readed);
        }

     if (strstr(cmd,"DATA_INV"))
        pbi(temp,dbuffer,nbytes); /* Want to inver the bits*/
     else
        pb(temp,dbuffer,nbytes); /* Want to use the bits*/
     

     temp[nbytes*2]='\0';

     if (cutputs(temp,ofp,MAX_WIDTH,col))
       {
        fprintf(stderr,"Could not write $DATA to file.\n");
        return 4;
       }

     
     free(temp);
     free(dbuffer);
     return 0;
    }
 /* end DATA and DATA_INV command */

 /* ADDRESS command */
 if (strstr(cmd,"ADDRESS"))
    {
     /* get the args inside the () */
     args_pos_s=strchr(cmd,'(');
     args_pos_e=strchr(cmd,')');
     if((!args_pos_s && args_pos_e )|| (!args_pos_e && args_pos_s))
         {
          fprintf(stderr,"Bad argument specification in $ADDRESS(DIR)$\n");
          return 1;
         }
     if (!args_pos_s && !args_pos_e)
       { /* Want to use the value */
        if (msize<=0x10000)
           *col+=fprintf(ofp,"%04x",(unsigned int)address);
        else
           *col+=fprintf(ofp,"%06x",(unsigned int)address);
        return 0;
       }
     /* Want to SET the value */
     memcpy(args,args_pos_s+1,(size_t)(args_pos_e-args_pos_s)); /* Copy te argument */
     args[args_pos_e-args_pos_s-1]='\0'; /* Put the null terminator */
     value=value_from(args);
     address=value;
     return 0;
    }
 /* end ADDRESS command */

 /* SET: CUTLINES command */
 if (strstr(cmd,"CUTLINES"))
    {
     /* get the args inside the () */
     args_pos_s=strchr(cmd,'(');
     args_pos_e=strchr(cmd,')');
     if((!args_pos_s && args_pos_e )|| (!args_pos_e && args_pos_s))
         {
          fprintf(stderr,"Bad argument specification in $CUTLINES(ON)$\n");
          return 1;
         }
     if (!args_pos_s && !args_pos_e)
       { /* Want to use the value */
        *col+=fprintf(ofp,"%04x",(unsigned int)cutlines);
        return 0;
       }
     /* Want to SET the value */
     memcpy(args,args_pos_s+1,(size_t)(args_pos_e-args_pos_s)); /* Copy te argument */
     args[args_pos_e-args_pos_s-1]='\0'; /* Put the null terminator */
     value=value_from(args);
     cutlines=value;
     return 0;
    }
 /* end CUTLINES command */

 /* STEP command */
 if (strstr(cmd,"STEP"))
    {
     /* get the args inside the () */
     args_pos_s=strchr(cmd,'(');
     args_pos_e=strchr(cmd,')');
     if((!args_pos_s && args_pos_e )|| (!args_pos_e && args_pos_s))
         {
          fprintf(stderr,"Bad argument specification in $STEP(VAL)$\n");
          return 1;
         }
     if (!args_pos_s && !args_pos_e)
       { /* Want to use the value */
        *col+=fprintf(ofp,"%04x",(unsigned int)step);
        return 0;
       }
     /* Want to SET the value */
     memcpy(args,args_pos_s+1,(size_t)(args_pos_e-args_pos_s)); /* Copy te argument */
     args[args_pos_e-args_pos_s-1]='\0'; /* Put the null terminator */
     value=value_from(args);
     step=value;
     return 0;
    }
 /* end STEP command */
 
 /* REWIND command */
 if (strstr(cmd,"REWIND"))
    {
     /* get the args inside the () */
     args_pos_s=strchr(cmd,'(');
     args_pos_e=strchr(cmd,')');
     if(args_pos_s || args_pos_e )
         {
          fprintf(stderr,"No arguments needed by $REWIND$\n");
          return 1;
         }
     fseek(dfp,stream_s,0);
     return 0;
    }
 /* end REWIND command */

 /* SBITS command */
 if (strstr(cmd,"SBITS"))
    {
     /* get the args inside the () */
     args_pos_s=strchr(cmd,'(');
     args_pos_e=strchr(cmd,')');
     if((!args_pos_s && args_pos_e )|| (!args_pos_e && args_pos_s))
         {
          fprintf(stderr,"Bad argument specification in $SBITS(VAL)$\n");
          return 1;
         }
     if (!args_pos_s && !args_pos_e)
       { /* Want to use the value */
        *col+=fprintf(ofp,"%d",(unsigned int)s_bits);
        return 0;
       }
     /* Want to SET the value */
     memcpy(args,args_pos_s+1,(size_t)(args_pos_e-args_pos_s)); /* Copy te argument */
     args[args_pos_e-args_pos_s-1]='\0'; /* Put the null terminator */
     value=value_from(args);
     s_bits=value;
     return 0;
    }
 /* end SBITS command */

 /* SADDBITS command */
 if (strstr(cmd,"SADDBITS"))
    {
     /* get the args inside the () */
     args_pos_s=strchr(cmd,'(');
     args_pos_e=strchr(cmd,')');
     if((!args_pos_s && args_pos_e )|| (!args_pos_e && args_pos_s))
         {
          fprintf(stderr,"Bad argument specification in $SADDBITS(VAL)$\n");
          return 1;
         }
     if (!args_pos_s && !args_pos_e)
       {
          fprintf(stderr,"Bad argument specification in $SADDBITS(VAL)$\n");
          return 1;
       }
     memcpy(args,args_pos_s+1,(size_t)(args_pos_e-args_pos_s)); /* Copy the argument */
     args[args_pos_e-args_pos_s-1]='\0'; /* Put the null terminator */
     value=value_from(args);
     *col+=fprintf(ofp,"%d",(unsigned int)(s_bits+value));
     return 0;
    }
 /* end SADDBITS command */

 /* SBYTES command */
 if (strstr(cmd,"SBYTES"))
    {
     /* get the args inside the () */
     args_pos_s=strchr(cmd,'(');
     args_pos_e=strchr(cmd,')');
     if((!args_pos_s && args_pos_e )|| (!args_pos_e && args_pos_s))
         {
          fprintf(stderr,"Bad argument specification in $SBYTES(VAL)$\n");
          return 1;
         }
     if (!args_pos_s && !args_pos_e)
       { /* Want to use the value */
        *col+=fprintf(ofp,"%d",(unsigned int)s_bytes);
        return 0;
       }
     /* Want to SET the value */
     memcpy(args,args_pos_s+1,(size_t)(args_pos_e-args_pos_s)); /* Copy the argument */
     args[args_pos_e-args_pos_s-1]='\0'; /* Put the null terminator */
     value=value_from(args);
     s_bytes=value;
     return 0;
    }
 /* end SBYTES command */

 /* FILL command */
 if (strstr(cmd,"FILL"))
    {
     /* get the args inside the () */
     args_pos_s=strchr(cmd,'(');
     args_pos_e=strchr(cmd,')');
     separator=strchr(cmd,',');
     if(!args_pos_s || !args_pos_e || !separator)
         {
          fprintf(stderr,"Bad argument specification in $FILL(VAL,TIMES)$\n");
          return 1;
         }
     /* Want to SET the value */
     memcpy(args,args_pos_s+1,4); /* Copy the character */
     args[4]='\0'; /* Put the null terminator */
     value=value_from(args);
     memcpy(args,separator+1,(size_t)(args_pos_e-separator)); /* Copy the times */
     times=value_from(args);
     if(times==-1) times=s_bytes;
     fill(ofp,value,times,col);
     return 0;
    }
 /* end FILL command */

 /* IDMASK command */
 if (strstr(cmd,"IDMASK"))
    {
     /* get the args inside the () */
     args_pos_s=strchr(cmd,'(');
     args_pos_e=strchr(cmd,')');
     if((!args_pos_s && args_pos_e )|| (!args_pos_e && args_pos_s))
         {
          fprintf(stderr,"Bad argument specification in $IDMASK(VAL)$\n");
          return 1;
         }
     if (!args_pos_s && !args_pos_e)
       { /* Want to use the value */
        *col+=fprintf(ofp,"%s",idmask);
        return 0;
       }
     /* Want to SET the value */
     memcpy(args,args_pos_s+1,(size_t)(args_pos_e-args_pos_s)); /* Copy te argument */
     args[args_pos_e-args_pos_s-1]='\0'; /* Put the null terminator */
     if (id) free(id);
     if((id=(char *)malloc(strlen(args)))==NULL)
       {
        fprintf(stderr,"Cannot asign memory needed for $IDMASK$\n");
        return 3;
       }
     strcpy(id,args);
     return 0;
    }
 /* end IDMASK command */


 /* ID command */
 if (strstr(cmd,"ID"))
    {
     /* get the args inside the () */
     args_pos_s=strchr(cmd,'(');
     args_pos_e=strchr(cmd,')');
     if((!args_pos_s && args_pos_e )|| (!args_pos_e && args_pos_s))
         {
          fprintf(stderr,"Bad argument specification in $ID(VAL)$\n");
          return 1;
         }
     if (!args_pos_s && !args_pos_e)
       { /* Want to use the value */
        *col+=fprintf(ofp,"%s",id);
        return 0;
       }
     /* Want to SET the value */
     memcpy(args,args_pos_s+1,(size_t)(args_pos_e-args_pos_s)); /* Copy te argument */
     args[args_pos_e-args_pos_s-1]='\0'; /* Put the null terminator */
     if (id) free(id);
     if((id=(char *)malloc(strlen(args)))==NULL)
       {
        fprintf(stderr,"Cannot asign memory needed for $ID$\n");
        return 3;
       }
     strcpy(id,args);
     return 0;
    }
 /* end ID command */

 /* BSIZE2 command */
 if (strstr(cmd,"BSIZE2"))
    {
     /* get the args inside the () */
     args_pos_s=strchr(cmd,'(');
     args_pos_e=strchr(cmd,')');
     if (args_pos_s || args_pos_e)
       {
        fprintf(stderr,"$BSIZE2$ is read only\n");
        return 1;
       }
     *col+=fprintf(ofp,"%ld",strtol(bsize,NULL,0)*2);
     return 0;
    }
 /* end BSIZE2 command */
 
 /* BSIZE command */
 if (strstr(cmd,"BSIZE"))
    {
     /* get the args inside the () */
     args_pos_s=strchr(cmd,'(');
     args_pos_e=strchr(cmd,')');
     if((!args_pos_s && args_pos_e )|| (!args_pos_e && args_pos_s))
         {
          fprintf(stderr,"Bad argument specification in $BSIZE(VAL)$\n");
          return 1;
         }
     if (!args_pos_s && !args_pos_e)
       { /* Want to use the value */
        *col+=fprintf(ofp,"%s",bsize);
        return 0;
       }
     /* Want to SET the value */
     memcpy(args,args_pos_s+1,(size_t)(args_pos_e-args_pos_s)); /* Copy te argument */
     args[args_pos_e-args_pos_s-1]='\0'; /* Put the null terminator */
     if (id) free(id);
     if((id=(char *)malloc(strlen(args)))==NULL)
       {
        fprintf(stderr,"Cannot asign memory needed for $BSIZE$\n");
        return 3;
       }
     strcpy(bsize,args);
     return 0;
    }
 /* end BSIZE command */
 
 return 0;
}


int
repeat (FILE *ifp, FILE *ofp, FILE *dfp,long limit)
{
 char line[2001];
 char *cmd_pos_s=NULL;
 char *cmd_pos_e=NULL;
 char cmd[30];
 long entry_point;
 long end_address=limit; /*+step; SET: Why?*/

 /* Sanity check */
 if (limit>0 && address>=end_address)
   {
    /* SET: Not an error, the PROMs have some "fillers", when we use the full
            memory the fillers must be skipped */
    fprintf(stderr,"WARNING: REPEAT UNTIL 0x%lX but current address is 0x%lX\n",
            limit,address);
    if (fgets(line,2001,ifp)==NULL) return 1;
    do
      {
       if (!strncasecmp(line,"--END",5))
          break;
       if (fgets(line,2001,ifp)==NULL) return 1; /* get a new line to parse */
      }
    while(1);
    return 0;
   }

 entry_point=ftell(ifp);

 if (fgets(line,2001,ifp)==NULL) return 1;
 
 do
  {
   int col=0;
   cmd_pos_e=NULL; /* Ending position of command, also used as flag for a command found or not */

   if (!strncasecmp(line,"--END",5))
     {
      address+=step;
      /* Repeat condition */
      /* SET: XCF02S can produce address>end_address */
      if ( (limit==0) || ((limit <0) && feof(dfp)) || ((limit >0) && (address>=end_address)))
         break;
      if (ftell(dfp)-stream_s>=s_bytes)
        {
         fprintf(stderr,"INFO: Bitstream limit reached\n");
         //fprintf(stderr,"limit: %ld address %ld end_address %ld\n",limit,address,end_address);
         break;
        }
      /*Sanity check*/
      if ( (0 < limit) && (end_address < address))
        {
         fprintf(stderr,"ERROR: REPEAT UNTIL 0x%lX but current address is 0x%lX (2)\n",limit,address);
         return 24;
        }
      fseek(ifp,entry_point,0);
      if (fgets(line,2001,ifp)==NULL)
         return 1; /* get a new line to parse */
      continue;
     }
    
   while((cmd_pos_s=strchr(line,'$'))) /* $ character found in line */
    {

     if (cmd_pos_e)
       { /* There was another command on this line before */
         /* Write intermedia bytes */
        int size=cmd_pos_s-cmd_pos_e-1;
        if (fwrite(cmd_pos_e+1,1,size,ofp)!=cmd_pos_s-cmd_pos_e-1)
          {
           fprintf(stderr,"There was an error writing to svf file.\n");
           return 2;
          }
        col+=size;
       }
     else
       { /* write from begining of line to the starting $ char to output file */
        size_t size=cmd_pos_s-line;
        if (fwrite(line,1,size,ofp)!=(size_t)(cmd_pos_s-line))
          {
           fprintf(stderr,"There was an error writing to svf file.\n");
           return 2;
          }
        col+=size;
       }

     /* clear the starting '$' simbol so i can search the next */
     *cmd_pos_s='_';
     if ((cmd_pos_e=strchr(line,'$'))==NULL)
       {
        fprintf(stderr,"Parse error: Cant find ending '$' while parsing command.\n");
        return 3;
       }
     *cmd_pos_e='_'; /* clear the ending '$' simbol so i can search the next */
     memcpy(cmd,cmd_pos_s+1,(size_t)(cmd_pos_e-cmd_pos_s-1)); /* Copy the command */
     cmd[cmd_pos_e-cmd_pos_s-1]='\0'; /* Put the null terminator */
     if (do_command(cmd,dfp,ofp,&col))
        return 3;
    }
   /* No more commands */
   if (cmd_pos_e)
     {
      if (fputs(cmd_pos_e+1,ofp)==EOF) return 2; /* Write remainig bytes */
     }
   else
     {
      /* There was no command */
      if (fputs(line,ofp)==EOF) return 2; /* Write  all the line  */
     }
     
   if (fgets(line,2001,ifp)==NULL) return 1; /* get a new line to parse */
  }
 while (1);

 return 0;
}

