/* /% C %/ */
/***********************************************************************
 * cint (C/C++ interpreter)
 ************************************************************************
 * Source file end.c
 ************************************************************************
 * Description:
 *  Cleanup function
 ************************************************************************
 * Copyright(c) 1995~2001  Masaharu Goto 
 *
 * For the licensing terms see the file COPYING
 *
 ************************************************************************/

#include "common.h"

extern "C" {

/***********************************************************************
* G__free_preprocessfilekey()
***********************************************************************/
void G__free_preprocessfilekey(G__Preprocessfilekey *pkey)
{
  if(pkey->next) {
    G__free_preprocessfilekey(pkey->next);
    free((void*)pkey->next);
    pkey->next = (struct G__Preprocessfilekey*)NULL;
  }
  if(pkey->keystring) {
    free((void*)pkey->keystring);
    pkey->keystring=(char*)NULL;
  }
}

/***********************************************************************
* void G__scratch_all()
*
* Called by
*   G__main()
*   G__interpretexit();
*   G__exit()
*
***********************************************************************/
void G__scratch_all()
{
  struct G__var_array *local;

  G__LockCriticalSection();

  G__lasterrorpos.line_number = 0;
  G__lasterrorpos.filenum = -1;
  

  G__cintready=0; /* reset ready flag for embedded use */

#ifdef G__MEMTEST
  fprintf(G__memhist,"G__scratch_all() start\n");
#endif

  /*******************************************
   * clear interpriveve global variables
   *******************************************/
#ifdef G__MEMTEST
  fprintf(G__memhist,"Freeing local variables\n");
#endif
  local = G__p_local;
  while(local) {
    G__destroy(local,G__LOCAL_VAR);
    local=local->prev_local;
  }

  /*******************************************
   * clear interpriveve global variables
   *******************************************/
#ifdef G__MEMTEST
  fprintf(G__memhist,"Freeing temp object %d\n",G__templevel);
#endif
  if(G__p_tempbuf) {
    if(G__templevel>0) G__templevel = 0;
    G__free_tempobject();
  }

  /*******************************************
   * clear interpriveve global variables
   *******************************************/
#ifdef G__MEMTEST
  fprintf(G__memhist,"Freeing global variables\n");
#endif
  G__destroy(&G__global,G__GLOBAL_VAR);

  /*******************************************
   * free exception handling buffer
   *******************************************/
  G__free_exceptionbuffer();

#ifdef G__SECURITY
  /*******************************************
   * garbage collection
   *******************************************/
  if(G__security&G__SECURE_GARBAGECOLLECTION) {
    G__garbagecollection();
  }
#endif

  /*************************************************************
   * Free struct tag name and member table
   *************************************************************/
#ifdef G__MEMTEST
  fprintf(G__memhist,"Freeing struct tag table\n");
#endif
  G__free_struct_upto(0);

  /*************************************************************
   * Free string constant
   *************************************************************/
#ifdef G__MEMTEST
  fprintf(G__memhist,"Freeing string constants\n");
#endif
  G__free_string_upto(&G__conststringlist);

  /*************************************************************
   * Free typedef table
   *************************************************************/
#ifdef G__MEMTEST
  fprintf(G__memhist,"Freeing typedef table\n");
#endif
  G__free_typedef_upto(0);

  /*************************************************************
   * Initialize interpreted function table list
   *************************************************************/
#ifdef G__MEMTEST
  fprintf(G__memhist,"Freeing interpret function table\n");
#endif
  G__free_ifunc_table(&G__ifunc);
  G__ifunc.allifunc = 0;

  /********************************************
   * local variable is NULL at scratch 
   *********************************************/
  G__p_local = NULL;

  /********************************************
   * free include path list
   *********************************************/
#ifdef G__MEMTEST
  fprintf(G__memhist,"Freeing include path list\n");
#endif
  G__free_ipath(&G__ipathentry);

#ifdef G__SHAREDLIB
  /********************************************
   * free dynamic link library
   *********************************************/
#ifdef G__MEMTEST
  fprintf(G__memhist,"Freeing shared library\n");
#endif
  G__free_shl_upto(0);
#endif

  /********************************************
   * free preprocessfilekey list
   *********************************************/
#ifdef G__MEMTEST
  fprintf(G__memhist,"Freeing preprocess file key list\n");
#endif
  G__free_preprocessfilekey(&G__preprocessfilekey);


#ifdef G__MEMTEST
  fprintf(G__memhist,"Closing macro file\n");
#endif
  if(G__mfp) {
#ifdef G__MEMTEST
    fprintf(G__memhist,"Ignore this error:");
    G__fprinterr(G__serr,"Ignore this error:");
#endif
    G__closemfp();
    G__mfp=NULL;
  }


  /* close source files */
#ifdef G__MEMTEST
  fprintf(G__memhist,"Closing input files\n");
#endif
  G__close_inputfiles();

#ifdef G__DUMPFILE
#ifdef G__MEMTEST
  fprintf(G__memhist,"Closing function call dump file\n");
  if(G__dumpfile && G__memhist!=G__dumpfile) fclose(G__dumpfile);
#else
  if(G__dumpfile) fclose(G__dumpfile);
#endif
  G__dumpfile=(FILE *)NULL;
#endif

  /* set function key */
  if(G__key!=0) system("key .cint_key -l execute");

#ifdef G__MEMTEST
  fprintf(G__memhist,"Closing readline dump files\n");
#endif
  while(G__dumpreadline[0]) {
    fclose(G__dumpreadline[0]);
    G__popdumpinput();
  }

#ifdef G__MEMTEST
  fprintf(G__memhist,"Freeing function macro list\n");
#endif
  G__freedeffuncmacro(&G__deffuncmacro);

#ifdef G__TEMPLATECLASS
#ifdef G__MEMTEST
  fprintf(G__memhist,"Freeing template class list\n");
#endif
  G__freedeftemplateclass(&G__definedtemplateclass);
#endif

#ifdef G__TEMPLATEFUNC
#ifdef G__MEMTEST
  fprintf(G__memhist,"Freeing template function list\n");
#endif
  G__freetemplatefunc(&G__definedtemplatefunc);
#endif

  /* delete user defined pragma statements */
  G__freepragma(G__paddpragma);
  G__paddpragma=(struct G__AppPragma*)NULL;

  if(G__allincludepath) {
    free(G__allincludepath);
    G__allincludepath=(char*)NULL;
  }

  G__DeleteConstStringList(G__SystemIncludeDir);
  G__SystemIncludeDir = (struct G__ConstStringList*)NULL;

  /* This implementation is premature in a sense that macro can not be 
   * rewound to file position */
  G__init_replacesymbol();

  /*************************************************************
   * Initialize cint body global variables
   *************************************************************/
  G__init = 0;
  G__init_globals();

  G__reset_setup_funcs();

  G__clear_errordictpos();

#ifdef G__MEMTEST
  G__memresult();
#endif

  G__UnlockCriticalSection();
}

/***********************************************************************
* G__free_friendtag()
*
***********************************************************************/
void G__free_friendtag(G__friendtag *friendtag)
{
  if(friendtag 
     && friendtag!=friendtag->next
     ) {
    G__free_friendtag(friendtag->next);
    free((void*)friendtag);
  }
}

/***********************************************************************
* int G__free_ifunc_table(ifunc)
*
* Called by
*   G__free_ifunc_table()  recursive call
*   G__free_struct()
*   G__scratch_all()
*
***********************************************************************/
int G__free_ifunc_table(G__ifunc_table_internal *ifunc)
{
  int i,j;
  int flag;
  if(ifunc->next) {
    G__free_ifunc_table(ifunc->next);
    free((void*)ifunc->next);
    ifunc->next=(struct G__ifunc_table_internal *)NULL;
  }
  /* Freeing default parameter storage */
  for(i=ifunc->allifunc-1;i>=0;i--) {
#ifdef G__MEMTEST
    fprintf(G__memhist,"func %s\n",ifunc->funcname[i]);
#endif
      flag = 0;
    if(ifunc->funcname[i]) {
      free((void*)ifunc->funcname[i]);
      ifunc->funcname[i] = (char*)NULL;
      flag = 1;
    }
#ifdef G__ASM_WHOLEFUNC
    if(
       flag &&
       ifunc->pentry[i] && 
       ifunc->pentry[i]->bytecode) {
      G__free_bytecode(ifunc->pentry[i]->bytecode);
      ifunc->pentry[i]->bytecode = (struct G__bytecodefunc*)NULL;
    }
#endif
#ifdef G__FRIEND
    if(flag) G__free_friendtag(ifunc->friendtag[i]);
#endif
    if(flag) {
      for(j=ifunc->para_nu[i]-1;j>=0;j--) {
        if(ifunc->param[i][j]->name) {
          free((void*)ifunc->param[i][j]->name);
          ifunc->param[i][j]->name=(char*)NULL;
        }
        if(ifunc->param[i][j]->def) {
          free((void*)ifunc->param[i][j]->def);
          ifunc->param[i][j]->def=(char*)NULL;
        }
        if(ifunc->param[i][j]->pdefault &&
           (&G__default_parameter)!=ifunc->param[i][j]->pdefault &&
           (G__value*)(-1)!=ifunc->param[i][j]->pdefault) {
          free((void*)ifunc->param[i][j]->pdefault);
          ifunc->param[i][j]->pdefault=(G__value*)NULL;
        }
      }
    }
  }
  ifunc->page=0;

  /* Do not free 'ifunc' because it can be a global/static object */
  return(0);
}

/***********************************************************************
* int G__free_member_table(ifunc)
***********************************************************************/
int G__free_member_table(G__var_array *mem)
{
  if(mem->next) {
    G__free_member_table(mem->next);
    free((void*)mem->next);
    mem->next=(struct G__var_array *)NULL;
  }
  return(0);
}

/***********************************************************************
* int G__free_ipath()
*
* Called by
*   G__free_ipath()   recursive call
*   G__scratch_all()
*
***********************************************************************/
int G__free_ipath(G__includepath *ipath)
{
  if(ipath->next) {
    G__free_ipath(ipath->next);
    free((void*)ipath->next);
    ipath->next=(struct G__includepath *)NULL;
    free((void*)ipath->pathname);
    ipath->pathname=(char *)NULL;
  }
  return(0);
}

/***********************************************************************
* void G__destroy(var,isglobal)
***********************************************************************/
/* destroy local variable and free memory*/
void G__destroy(G__var_array *var,int isglobal)
{
   G__destroy_upto(var, isglobal, 0, -1);

#ifdef G__WE_WANT_CODE_DUPLICATION
  int itemp=0,itemp1=0;
  
#ifdef G__OLDIMPLEMENTATION1802
  char temp[G__ONELINE];
#endif
  int store_tagnum;
  long store_struct_offset; /* used to be int */
  int store_return;
  int store_prerun;
  int remain=0;
  int cpplink;
  int i,size;
  long address;

#ifndef G__OLDIMPLEMENTATION2038
  /* This part is not needed in G__destroy_upto.  enclosing_scope and
   * inner_scope members are assigned only as local variable table for
   * bytecode function  in which case  G__destroy() is always used to
   * deallocate the table */
  var->enclosing_scope = (struct G__var_array*)NULL;
  if(var->inner_scope) {
    i=0;
    while(var->inner_scope[i]) {
      G__destroy(var->inner_scope[i],isglobal);
      free((void*)var->inner_scope[i]);
      ++i;
    }
  }
#endif
  
  /*******************************************
   * If there are any sub var array list,
   * destroy it too.
   *******************************************/
  if(var->next) {
    if(var->allvar==G__MEMDEPTH) {
      G__destroy(var->next,isglobal);
      free((void*)var->next);
      var->next=NULL;
    }
    else {
      fprintf(stderr,"!!!Fatal Error: Interpreter memory overwritten by illegal access.!!!\n");
      fprintf(stderr,"!!!Terminate session!!!\n");
    }
  }
  
  for (itemp = var->allvar - 1; itemp >= remain; itemp--) {
    
    /*****************************************
     * If the variable is a function scope
     * static variable, it will be kept.
     * else (auto and static body) free
     * allocated memory area.
     *****************************************/
    if (
#ifdef G__ASM_WHOLEFUNC
      ((var->statictype[itemp] != G__LOCALSTATIC) || (isglobal == G__GLOBAL_VAR)) && 
      (var->statictype[itemp] != G__COMPILEDGLOBAL) &&
      (G__BYTECODELOCAL_VAR != isglobal)
#else
      ((var->statictype[itemp] != G__LOCALSTATIC) || isglobal) && 
      (var->statictype[itemp] != G__COMPILEDGLOBAL)
#endif
    ) {
      
      cpplink=0;
      /****************************************************
       * If C++ class, destructor has to be called
       ****************************************************/
      if(var->type[itemp]=='u' && 0==G__ansiheader && 0==G__prerun) {
#ifndef G__OLDIMPLEMENTATION1802
        char vv[G__BUFLEN];
        char *temp=vv;
#endif
        
        store_struct_offset = G__store_struct_offset;
        G__store_struct_offset = var->p[itemp]; /* duplication */
        
        store_tagnum = G__tagnum;
        G__tagnum = var->p_tagtable[itemp];
        
        store_return=G__return;
        G__return=G__RETURN_NON;

#ifndef G__OLDIMPLEMENTATION1802
        if(strlen(G__struct.name[G__tagnum])>G__BUFLEN-5)
          temp = (char*)malloc(strlen(G__struct.name[G__tagnum])+5);
#endif
        
        sprintf(temp, "~%s()", G__struct.name[G__tagnum]);
        if (G__dispsource) {
          G__fprinterr(
            G__serr,
            "\n!!!Calling destructor 0x%lx.%s for %s ary%d:link%d",
            G__store_struct_offset,
            temp,
            var->varnamebuf[itemp],
            var->varlabel[itemp][1] /* num of elements */,
            G__struct.iscpplink[G__tagnum]
          );
        }
        
        
        store_prerun = G__prerun;
        G__prerun=0;
        /********************************************************
         * destruction of array 
         ********************************************************/
        if (G__struct.iscpplink[G__tagnum] == G__CPPLINK) {
          if (var->statictype[itemp] == G__AUTOARYDISCRETEOBJ) {
            long store_globalvarpointer = G__globalvarpointer;
            size = G__struct.size[G__tagnum];
            int num_of_elements = var->varlabel[itemp][1] /* number of elements */;
            if (!num_of_elements) {
              num_of_elements = 1;
            }
            for (i = num_of_elements - 1; i >= 0; --i) {
              G__store_struct_offset = var->p[itemp] + (i * size);
              G__globalvarpointer = G__store_struct_offset;
              G__getfunction(temp, &itemp1, G__TRYDESTRUCTOR);
              if (!itemp1) {
                break;
              }
            }
            G__globalvarpointer = store_globalvarpointer;
            // FIXME: This should probably be a delete[] (char*).
            free(var->p[itemp]);
          }
          else {
            G__store_struct_offset = var->p[itemp];
            i = var->varlabel[itemp][1] /* number of elements */;
            if (i > 0) {
              G__cpp_aryconstruct = i;
            }
            G__getfunction(temp, &itemp1, G__TRYDESTRUCTOR); 
            G__cpp_aryconstruct = 0;
          }
          cpplink = 1;
        }
        else {
          size = G__struct.size[G__tagnum];
          int num_of_elements = var->varlabel[itemp][1] /* number of elements */;
          if (!num_of_elements) {
            num_of_elements = 1;
          }
          for (i = num_of_elements - 1; i >= 0; --i) {
            G__store_struct_offset = var->p[itemp] + (i * size);
            if (G__dispsource) {
              G__fprinterr(G__serr, "\n0x%lx.%s", G__store_struct_offset, temp);
            }
            G__getfunction(temp, &itemp1, G__TRYDESTRUCTOR);
            if (!itemp1) {
              break;
            }
          }
        }
#ifndef G__OLDIMPLEMENTATION1802
        if (vv != temp) {
          free(temp);
        }
#endif
        G__prerun = store_prerun;
        G__store_struct_offset = store_struct_offset;
        G__tagnum = store_tagnum;
        G__return = store_return;
      } // end of type == 'u'

#ifdef G__SECURITY
      else if (
        G__security & G__SECURE_GARBAGECOLLECTION && 
        !G__no_exec_compile &&
        isupper(var->type[itemp]) &&
        var->p[itemp]
      ) {
        i = var->varlabel[itemp][1] /*num of elements*/;
        if (!i) {
          i = 1;
        }
        do {
          --i;
          address = var->p[itemp] + (i * G__LONGALLOC);
          if (*((long*) address)) {
            G__del_refcount((void*) (*((long*) address)), (void**) address);
          }
        } while (i);
      }
#endif

#ifdef G__MEMTEST
      fprintf(G__memhist,"Free(%s)\n",var->varnamebuf[itemp]);
#endif
      // ??? Scott Snyder fixed as var->p[itemp]>0x10000 ???
      if ((cpplink == G__NOLINK) && var->p[itemp] && (var->p[itemp] != -1)) {
        // FIXME: This should probably be a delete[] (char*).
        free((void*) var->p[itemp]);
      }
      
    } // end of statictype==LOCALSTATIC or COMPILEDGLOBAL
    
#ifdef G__DEBUG
    else if(G__memhist) {
      fprintf(G__memhist
              ,"0x%lx (%s) not freed localstatic or compiledglobal FILE:%s LINE:%d\n"
              ,var->p[itemp],var->varnamebuf[itemp]
              ,G__ifile.name,G__ifile.line_number);
    } 
#endif
    
    /* initialize varpointer and varlabel */
    /* var->varpointer[itemp]=0;*/
    for(itemp1=0;itemp1<G__MAXVARDIM;itemp1++) {
      var->varlabel[itemp][itemp1]=0;
    }
    if(var->varnamebuf[itemp]) {
      // FIXME: This should probably be a delete[] (char*).
      free((void*) var->varnamebuf[itemp]);
      var->varnamebuf[itemp] = 0;
    }

  }
  
  var->allvar = remain;
#endif // G__WE_WANT_CODE_DUPLICATION
}


/******************************************************************
* G__exit()
******************************************************************/
void G__exit(int rtn)
{
  G__scratch_all();
  fflush(G__sout);
  fflush(G__serr);
  exit(rtn);
}


/***********************************************************************
* G__call_atexit()
*
* Execute atexit function. atexit is reset before calling
* the function to avoid recursive atexit call.
***********************************************************************/
int G__call_atexit()
{
  char temp[G__ONELINE];
  if(G__breaksignal) G__fprinterr(G__serr,"!!! atexit() call\n");
  G__ASSERT(G__atexit);
  sprintf(temp,"%s()",G__atexit);
  G__atexit=NULL;
  G__getexpr(temp);
  return(0);
}


/***********************************************************************
* G__close_inputfiles()
*
* Called by
*    G__main()        When input file is not found.
*                     when '-c' option, this is not used.
*    G__interpretexit()
*    G__exit()
***********************************************************************/
int G__close_inputfiles()
{
  int iarg;
#ifdef G__DUMPFILE
  if(G__dumpfile) G__dump_tracecoverage(G__dumpfile);
#endif
  for(iarg=0;iarg<G__nfile;iarg++) {
    if(G__srcfile[iarg].dictpos) {
      if(G__srcfile[iarg].dictpos->ptype &&
         G__srcfile[iarg].dictpos->ptype!=(char*)G__PVOID) {
        free((void*)G__srcfile[iarg].dictpos->ptype);
      }
      free((void*)G__srcfile[iarg].dictpos);
      G__srcfile[iarg].dictpos=(struct G__dictposition*)NULL;
    }
    if(G__srcfile[iarg].hasonlyfunc) {
      free((void*)G__srcfile[iarg].hasonlyfunc);
      G__srcfile[iarg].hasonlyfunc=(struct G__dictposition*)NULL;
    }
    if(G__srcfile[iarg].fp) { 
      fclose(G__srcfile[iarg].fp);
      if(G__srcfile[iarg].prepname) {
        int j;
        for(j=iarg+1;j<G__nfile;j++) {
          if(G__srcfile[j].fp==G__srcfile[iarg].fp) 
            G__srcfile[j].fp=(FILE*)NULL;
        }
      }
      G__srcfile[iarg].fp=(FILE*)NULL;
    }
    if(G__srcfile[iarg].breakpoint) {
      free((void*)G__srcfile[iarg].breakpoint);
      G__srcfile[iarg].breakpoint=(char*)NULL;
      G__srcfile[iarg].maxline=0;
    }
    if(G__srcfile[iarg].prepname) {
      if('('!=G__srcfile[iarg].prepname[0]) remove(G__srcfile[iarg].prepname);
      free((void*)G__srcfile[iarg].prepname);
      G__srcfile[iarg].prepname=(char*)NULL;
    }
    if(G__srcfile[iarg].filename) {
#ifndef G__OLDIMPLEMENTATION1546
      int len = strlen(G__srcfile[iarg].filename);
      if(len>(int)strlen(G__NAMEDMACROEXT2) && 
         strcmp(G__srcfile[iarg].filename+len-strlen(G__NAMEDMACROEXT2),
                G__NAMEDMACROEXT2)==0) {
        remove(G__srcfile[iarg].filename);
      }
#endif
      free((void*)G__srcfile[iarg].filename);
      G__srcfile[iarg].filename=(char*)NULL;
    }
    G__srcfile[iarg].hash=0;
  }
  G__nfile=0;

  if(G__xfile[0]) {
    remove(G__xfile);
    G__xfile[0]='\0';
  }
  if(G__tempc[0]) {
    remove(G__tempc);
    G__tempc[0]='\0';
  }

  /*****************************************************************
  * Closing modified STDIOs.  May need to modify here.
  *  init.c, end.c, scrupto.c, pause.c
  *****************************************************************/
  if(G__serr!=G__stderr && G__serr) {
    fclose(G__serr);
    G__serr=G__stderr;
  }
  if(G__sout!=G__stdout && G__sout) {
    fclose(G__sout);
    G__sout=G__stdout;
  }
  if(G__sin!=G__stdin && G__sin) {
    fclose(G__sin);
    G__sin=G__stdin;
  }
  return(0);
}

/***********************************************************************/
/* G__interpretexit()                                                  */
/*                                                                     */
/* Called by                                                           */
/*     G__main()                                                       */
/*                                                                     */
/***********************************************************************/
int G__interpretexit()
{
  if(G__atexit) G__call_atexit();
  G__scratch_all();
  if(G__breaksignal) G__fprinterr(G__serr,"\nEND OF EXECUTION\n");
  return(0);
}

} /* extern "C" */

/*
 * Local Variables:
 * c-tab-always-indent:nil
 * c-indent-level:2
 * c-continued-statement-offset:2
 * c-brace-offset:-2
 * c-brace-imaginary-offset:0
 * c-argdecl-indent:0
 * c-label-offset:-2
 * compile-command:"make -k"
 * End:
 */
