/* BrainFuck virtual machine by sKUrZ0 . CopyLeft-2004 */

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>


void terminate(char *rao){
  printf("%s\n",rao);
  exit(0);
}

int do_bf(char *code, unsigned codelen,
          char *data, unsigned datalen,
          char *stack, unsigned stacklen){
  unsigned codeptr=0;
  unsigned ptr=0;
  unsigned sp=0;
  char suport;
  unsigned long p;
  int fd;
  int s;
  for(s=0;s<datalen;s++) data[s]=0;
  while(codeptr!=codelen){
//#define DEBUG  
#ifdef DEBUG	  
    printf("sp:      0x%08x\n",sp);
    printf("codeptr: 0x%08x\n",codeptr);
#endif
    if(codeptr>=codelen) terminate("code error");
    switch(code[codeptr]){
      case '<':ptr--;codeptr++;break;
      case '>':ptr++;codeptr++;break;
      case '+':if(ptr>=datalen) terminate("segmentation fault");
               (data[ptr])++;codeptr++;break;
      case '-':if(ptr>=datalen) terminate("segmentation fault");
	       (data[ptr])--;codeptr++;break;
      case ',':if(ptr>=datalen) terminate("segmentation fault");
	       read(0,&data[ptr],1);codeptr++;break;
      case '.':if(ptr>=datalen) terminate("segmentation fault");
	       write(1,&data[ptr],1);codeptr++;break;
      case '[':if(sp==stacklen) terminate("stack overflow");
	       if(data[ptr]){
	         stack[sp++]=codeptr++;
	       }else{
                 p=0;
                 codeptr++;
                 for(;(codeptr<codelen)&&(p!=-1);) {
                   if(code[codeptr]=='[') p++;
                   if(code[codeptr]==']') p--;
                   codeptr++;
                 }
                 if(codeptr>codelen) codeptr=codelen;
	       }
	       break;
      case ']':if(sp==0) terminate("stack underflow");
               codeptr=stack[--sp];break;
      case '"':
                for(codeptr++;(codeptr<codelen)&&(code[codeptr]!='"');codeptr++){
                  if( (code[codeptr]=='\\') && ((codeptr+1)<codelen) ){
                    codeptr++;
		    switch(code[codeptr]){
		      case 'n':suport='\n';break;
                      case 'r':suport='\r';break;
                      case 't':suport='\t';break;
                      case 'a':suport='\a';break;
		    };
		    write(1,&suport,1);
		  }else
                    write(1,&code[codeptr],1);
                };codeptr++;break;
      default:codeptr++;break;//NOP
    }
  }
  if (sp) fprintf(stderr,"Error: acabat amb sp=%d\n",sp);
  return 0;
}

#ifndef _BF_NOMAIN_
int main(int argc, char *argv[]){
  char *code,*data,*stack;
  unsigned long codelen,stacklen,datalen;
  int fd;
  unsigned long arg=1;
  struct stat fdstat;
  code=data=stack=0;
  codelen=datalen=stacklen=0;
  fd=-1;
  //args
  if(argc>2){
    for(arg=1;arg<argc;arg++)
      if (argv[arg][0]=='-')
        switch(argv[arg][1]){
          case 'e':
            if((arg+1<argc)&&(fd=open(argv[++arg],O_RDONLY))!=-1){
	      fstat(fd,&fdstat);
	      codelen=fdstat.st_size;
	      code=(char*)malloc(codelen);
	      read(fd,code,codelen);
	      close(fd);
	    };
	    break;
      }
  }
  if(!datalen) datalen=2048;
  data=(char*)malloc(datalen);
  if(!stacklen) stacklen=512;
  stack=(char*)malloc(stacklen);
  if(!codelen){exit(0);/*pilla codi de stdin*/}/*FIXME*/
  //execució
  do_bf(code,codelen,data,datalen,stack,stacklen);
  //neteja
  if(code) free(code);
  if(data) free(data);
  if(stack) free(stack);
}
#endif
