//    crm_expr_isolate.c  - isolate a variable (includes mem management)
//  Copyright 2001-2006  William S. Yerazunis, all rights reserved.
//  
//  This software is licensed to the public under the Free Software
//  Foundation's GNU GPL, version 2.  You may obtain a copy of the
//  GPL by visiting the Free Software Foundations web site at
//  www.fsf.org, and a copy is included in this distribution.  
//
//  Other licenses may be negotiated; contact the 
//  author for details.  
//
//  include some standard files
#include "crm114_sysincludes.h"

//  include any local crm114 configuration file
#include "crm114_config.h"

//  include the crm114 data structures file
#include "crm114_structs.h"

//  and include the routine declarations file
#include "crm114.h"

//    the command line argc, argv
extern int prog_argc;
extern char **prog_argv;

//    the auxilliary input buffer (for WINDOW input)
extern char *newinputbuf;

//    the globals used when we need a big buffer  - allocated once, used 
//    wherever needed.  These are sized to the same size as the data window.
extern char *inbuf;
extern char *outbuf;
extern char *tempbuf;


//           Allow creation of a temporary isolated variable;
//	   this lives in the same big buffer as the environ
//           args, the arg0 args, and the basic formatting args
//           (like :_nl:, :_ht:, :_bs:, etc).

int crm_expr_isolate (CSL_CELL *csl, ARGPARSE_BLOCK *apb)
{
  char temp_vars [MAX_VARNAME];
  long tvlen;
  long vn_start_here;
  long vstart;
  long vlen;
  long mc;
  long done;
  long vallen;
  int iso_status;

  if (user_trace)
    fprintf (stderr, "executing an ISOLATE statement\n");


  //    get the list of variable names
  //
  crm_get_pgm_arg (temp_vars, MAX_VARNAME, apb->p1start, apb->p1len); 
  tvlen = crm_nexpandvar (temp_vars, apb->p1len, MAX_VARNAME);
  if (tvlen == 0)
    {
      nonfatalerror( "This statement is missing the variable to isolate"
		     " so I'll just ignore the whole statement.", "");
    }

  if (internal_trace)
    fprintf (stderr, "  creating isolated vars: ***%s***\n", temp_vars);

  done = 0;
  mc = 0;

  //  Now, find the vars (space-delimited, doncha know) and make them
  //  isolated.
  //
  vstart = 0;
  vlen = 0;
  vn_start_here = 0;
  while (!done)
    {
      crm_nextword (temp_vars, tvlen, vn_start_here, &vstart, &vlen);
      vn_start_here = vstart + vlen + 1;
      if (vlen == 0) 
	{
	  done = 1;
	}
      else    // not done yet.
	{
	  //        must make a copy of the varname.
	  char vname[MAX_VARNAME];
	  long vmidx;
	  memmove (vname, &(temp_vars[vstart]), vlen);
	  vname [vlen] = '\000';
	  if (vlen < 3)
	    {
	      nonfatalerror ("The variable you're asking me to ISOLATE "
			     " has an utterly bogus name.  I'll ignore"
			     " the rest of the statement", " ");
	      break;
	    };
	  if (strcmp (vname, ":_dw:") == 0)
	    {
	      nonfatalerror ("You can't ISOLATE the :_dw: data window! ",
			     "We'll just ignore that for now");
	    }
	  else    //  OK- isolate this variable
	    {
	      vmidx = crm_vht_lookup (vht, vname, vlen);
	      //
	      //        get initial value - that's the slashed value.
	      crm_get_pgm_arg (tempbuf, data_window_size, 
			       apb->s1start, apb->s1len);
	      //
	      //     Now, check these cases in order:
	      //
	      //     not preexisting, no /value/ -  isolate, set to ""
	      //     not preexisting, with /value/ -isolate, set /val/ 
	      //     preexisting _dw, with /value/ - isolate, set to /val/ 
	      //     preexisting _dw, no /value/ - isolate, set to dwval.
	      //     if preexisting AND default turned on - do nothing!
	      //     preexisting isolated, no /value/ - copy value.
	      //     preexisting isolated, with /value/ - alter /value/
	      //
	      //    not preexisting.  
	      if (vht[vmidx] == NULL)
		{
		  //   not preexisting, no slash value
		  if (internal_trace)
		    fprintf (stderr, "Not a preexisting var.\n");
		  
		  if (!apb->s1start)
		    {
		      // no slash value- set to ""
		      if (internal_trace)
			fprintf (stderr, "No initialization value given, using"
				 " a zero-length string.\n");
		      tempbuf[0] = '\0';
		      vallen = 0;
		    }
		  else
		    {
		      //  not preexisting, has a /value/, use it.
		      if (internal_trace)
			fprintf (stderr, "using the slash-value given.\n");
		      crm_get_pgm_arg (tempbuf, data_window_size, 
				       apb->s1start, apb->s1len);
		      vallen = crm_nexpandvar(tempbuf, apb->s1len, 
					      data_window_size - tdw->nchars);
		    };
		}
	      else
		//      it IS preexisting
		{
		  //   It is preexisting, maybe in isolation, maybe
		  //   not but we're isolating again.  So we need to
		  //   copy again.
		  //
		  if (internal_trace)
		    fprintf (stderr, "Preexisting var.\n");
		  if (apb->sflags & CRM_DEFAULT)
		    {
		      if (user_trace)
			fprintf (stderr, " var exists, default flag on, "
				 "so no action taken.\n");
		      goto no_isolate_action;
		      // return (0);
		    };
		  
		  if (apb->s1start)
		    {
		      //  yes, statement has a /value/
		      //    get the /value/
		      if (internal_trace)
			fprintf (stderr, "Using the provided slash-val.\n");
		      crm_get_pgm_arg (tempbuf, data_window_size, 
				       apb->s1start, apb->s1len);
		      vallen = 
			crm_nexpandvar(tempbuf, apb->s1len, 
				       data_window_size - tdw->nchars);
		    }
		  else
		    {
		      //     no /value/, so we need to use the old value.
		      //
		      if (internal_trace)
			fprintf (stderr, "No slash-value, using old value.\n");
		      strcpy (tempbuf, ":*");
		      strncat (tempbuf, vname, vlen);
		      vallen = 
			crm_nexpandvar (tempbuf, vlen+2, 
					data_window_size - tdw->nchars);
		    };
		};
	      //
	      //     Now we have the name of the variable in vname/vlen,
	      //    and the value string in tempbuf/vallen.  We can then
	      //    isolate it with crm_isolate_this, which always does
	      //    the right thing (pretty much).  :-)  
	      //
	      iso_status = crm_isolate_this (&vmidx, 
	      			vname, 0, vlen,
	      			tempbuf, 0, vallen);
	      if ( iso_status > 0) 
		return (iso_status);

	    };
	  //   the semicolon is to keep the C compiler happy.
	no_isolate_action: ;
	};
      vstart = vstart + vlen;
      if (temp_vars[vstart] == '\000'
	  || vstart >= tvlen )
	done = 1;
    };
  return (0);
};


//
//      General-purpose routine to "do the right thing" to isolate a 
//      string value.  This routine takes care of all of the possible 
//      extenuating circumstances, like is/is not already isolated,
//      is/is not requiring a reclaim, etc.  (how it knows?  If the
//      value is in the TDW, it may need to be reclaimed, otherwise it
//      doesn't reclaim.)
//
//      Note - this routine will malloc and then free a spare buffer if
//      it gets handed data that's already in the TDW.  Best not to do
//      that if you can avoid it; that is an efficiency speedup
//
int crm_isolate_this (long *vptr, 
		  char *nametext, long namestart, long namelen, 
		  char *valuetext, long valuestart, long valuelen)

{

  long is_old;
  long is_old_isolated;
  long vmidx;
  long oldvstart = 0; 
  long oldvlen = 0;

  long neededlen;

  if (internal_trace)
    {
      fprintf (stderr, "using crm_isolate_this, vptr = %lX\n", (long) vptr);
    }

  
  //    keep track of the amount of storage needed for this variable
  //    to be inserted into the isolated space.

  neededlen = 10;
  //
  //        gather information 
  //         In particular - does the name already exits?  Is it
  //         already isolated?
  if (vptr)
    {
      vmidx = *vptr;
    }
  else
    {
      vmidx = crm_vht_lookup ( vht, &nametext[namestart], namelen);
    }

  //     check the vht - if it's not here, we need to add it.
  //

  if (vht[vmidx] == NULL) 
    {
      //  optimization - if it's not old, we don't need to run a reclaim
      //  phase later on.
      is_old = 0;
      
      //  must allow space for the name.
      neededlen += namelen;         
    }
  else
    {
      is_old = 1;
    };

  if (is_old && vht[vmidx]->valtxt == tdw->filetext)
    {
      is_old_isolated = 1;
      //           and save old start and length for the incremental reclaimer
      oldvstart = vht[vmidx]->vstart;
      oldvlen = vht[vmidx]->vlen;
      
      //  how much space will the new value take up?
      neededlen += valuelen - oldvlen;
    }
  else
    {
      is_old_isolated = 0;
      //  and how much space will we need?
      neededlen += valuelen;
    };

  //     Do a check - is there enough space in the tdw to hold
  //     the new variable (both name and value)?
  if ( tdw->nchars + neededlen > data_window_size )
    {
      char vname[129];
      strncpy (vname, &nametext[namestart], 
	       ( (128 < namelen) ? 128 : namelen));
      vname[129] = 0;
      fatalerror ("You have blown the memory-storage gaskets while trying"
		  "to store the ISOLATEd variable ", vname);
      return (1);
    }
  //   If we get to here, there's more than enough space; so we're good to 
  //   go with no further checks.


  //     If there wasn't a vht slot existing, make one.
  //
  if (!is_old)
    {
      //  nope, never seen this var before, add it into the VHT
      //       namestart is where we are now.
      long nstart, vstart;
      if (internal_trace)
        fprintf (stderr, "... this is a new isolated var\n");
      //
      //      Put the name into the tdw memory area, add a & after it.
      //
      //       do the name first.  Start on a newline.
      tdw->filetext[tdw->nchars] = '\n';
      tdw->nchars++;
      nstart = tdw->nchars;
      memmove (&tdw->filetext[nstart], &nametext[namestart], namelen);
      tdw->nchars = tdw->nchars + namelen;
      tdw->filetext[tdw->nchars] = '=';
      tdw->nchars++;

      //      and put in the value in now
      vstart = tdw->nchars;
      memmove (&tdw->filetext[vstart], &valuetext[valuestart], valuelen);
      tdw->nchars = tdw->nchars + valuelen;
      tdw->filetext[tdw->nchars] = '\n';
      tdw->nchars++;

      //      now, we whack the actual VHT.
      crm_setvar (NULL, 0,
                  tdw->filetext, nstart, namelen,
                  tdw->filetext, vstart, valuelen,
                  csl->cstmt);
      //     that's it.    It's now in the TDW and in the VHT
      return (0);
    }
  
  //  No, it's a preexisting variable.  We need to do the shuffle.
  // 
  //    Note that this code is almost but not quite a mirror
  //    of the code that lives in crm_set_temp_nvar.
  //
  //
  if (internal_trace)
    fprintf (stderr, "Resetting valtxt to point at tdw.\n");
  vht[vmidx]->valtxt = tdw->filetext;
  if (internal_trace)
    fprintf (stderr, "Fresh start: offset %ld length %ld.\n",
	     tdw->nchars, valuelen);
  //   
  //
  //   If we have a zero-length string, followed by a
  //   non-zero-lenth string, next to each other, with
  //   no intervening allocations, both strings will
  //   have the _same_ start point.  This messes things
  //   up badly on subsequent alters.  Thus, we
  //   _must_ put a spacer in.
  //
  //   This code must also be echoed in crm_set_temp_nvar
  //
  if (valuelen == 0) 
    {
      tdw->filetext[tdw->nchars] = '\n';
      tdw->nchars++;
    };
  //            (end of danger zone)
  //
  vht[vmidx]->vstart = tdw->nchars;
  vht[vmidx]->vlen   = valuelen;
  if (internal_trace)
    fprintf (stderr, "Memmoving the value in.\n");
  memmove (&(tdw->filetext[tdw->nchars]), 
	   tempbuf,
	   valuelen);
  tdw->nchars = tdw->nchars + valuelen;
  //
  // trailing separator
  tdw->filetext[tdw->nchars] = '\n';
  tdw->nchars++;
  //
  //      and reset 'previous match' start and length
  if (internal_trace)
    fprintf (stderr, "reset the previous-match to start.\n");
  vht[vmidx]->mstart = vht[vmidx]->vstart;
  vht[vmidx]->mlen   = 0;


  //     Step 2 - if this was isolated, reclaim the
  //     old storage if nobody else is using it.
  // 
  if (is_old_isolated)
    {
      if (internal_trace)
	fprintf (stderr, "This was already an isolated var, so "
		 "do a reclamation on the old space.\n"); 
      //  vstart==0 means "ignore this value" to reclamation
      //
      crm_compress_tdw_section (vht[vmidx]->valtxt,
				oldvstart , 
				oldvstart+oldvlen);
    };

  return (0);
};



syntax highlighted by Code2HTML, v. 0.9.1