/* -----------------------------------------------------------------------------
 * typemap.c
 *
 *     A somewhat generalized implementation of SWIG1.1 typemaps.
 *
 * Author(s) : David Beazley (beazley@cs.uchicago.edu)
 *
 * Copyright (C) 1999-2000.  The University of Chicago
 * See the file LICENSE for information on usage and redistribution.
 * ----------------------------------------------------------------------------- */

char cvsroot_typemap_c[] = "/cvsroot/SWIG/Source/Swig/typemap.c,v 1.29 2004/02/10 09:21:36 mmatus Exp";

#include "swig.h"
#include "cparse.h"
#include <ctype.h>

static void replace_embedded_typemap(String *s);

/* -----------------------------------------------------------------------------
 * Typemaps are stored in a collection of nested hash tables.  Something like
 * this:
 *
 * [ type ]
 *    +-------- [ name ]
 *    +-------- [ name ]
 *    
 * Each hash table [ type ] or [ name ] then contains references to the
 * different typemap methods.    These are referenced by names such as
 * "tmap:in", "tmap:out", "tmap:argout", and so forth.
 *
 * The object corresponding to a specific method has the following
 * attributes:
 *
 *    "type"    -  Typemap type
 *    "pname"   -  Parameter name
 *    "code"    -  Typemap code
 *    "typemap" -  Descriptive text describing the actual map
 *    "locals"  -  Local variables (if any)
 * 
 * ----------------------------------------------------------------------------- */

#define MAX_SCOPE  32


static Hash *typemaps[MAX_SCOPE];
static int   tm_scope = 0;

/* -----------------------------------------------------------------------------
 * Swig_typemap_init()
 *
 * Initialize the typemap system
 * ----------------------------------------------------------------------------- */

void Swig_typemap_init() {
  int i;
  for (i = 0; i < MAX_SCOPE; i++) {
    typemaps[i] = 0;
  }
  typemaps[0] = NewHash();
  tm_scope = 0;
}

static String *tmop_name(const String_or_char *op) {
  static Hash *names = 0;
  String *s;
  /* Due to "interesting" object-identity semantics of DOH,
     we have to make sure that we only intern strings without object
     identity into the hash table.
     
     (Swig_typemap_attach_kwargs calls tmop_name several times with
     the "same" String *op (i.e., same object identity) but differing
     string values.)

     Most other callers work around this by using char* rather than
     String *.
                  -- mkoeppe, Jun 17, 2003
  */
  const char *op_without_object_identity = Char(op);
  if (!names) names = NewHash();
  s = Getattr(names, op_without_object_identity);
  if (s) return s;
  s = NewStringf("tmap:%s",op);
  Setattr(names,op_without_object_identity,s);
  return s;
}

/* -----------------------------------------------------------------------------
 * Swig_typemap_new_scope()
 * 
 * Create a new typemap scope
 * ----------------------------------------------------------------------------- */

void Swig_typemap_new_scope() {
  tm_scope++;
  typemaps[tm_scope] = NewHash();
}

/* -----------------------------------------------------------------------------
 * Swig_typemap_pop_scope()
 *
 * Pop the last typemap scope off
 * ----------------------------------------------------------------------------- */

Hash *
Swig_typemap_pop_scope() {
  if (tm_scope > 0) {
    return typemaps[tm_scope--];
  }
  return 0;
}

/* ----------------------------------------------------------------------------- 
 * Swig_typemap_register()
 *
 * Add a new multi-valued typemap
 * ----------------------------------------------------------------------------- */

void
Swig_typemap_register(const String_or_char *op, ParmList *parms, String_or_char *code, ParmList *locals, ParmList *kwargs) {
  Hash *tm;
  Hash *tm1;
  Hash *tm2;
  Parm *np;
  String *tmop;
  SwigType *type;
  String   *pname;

  if (!parms) return;
  tmop = tmop_name(op);

  /* Register the first type in the parameter list */

  type = Getattr(parms,"type");
  pname = Getattr(parms,"name");

  /* See if this type has been seen before */
  tm = Getattr(typemaps[tm_scope],type);
  if (!tm) {
    tm = NewHash();
    Setattr(typemaps[tm_scope],Copy(type),tm);
    Delete(tm);
  }
  if (pname) {
    /* See if parameter has been seen before */
    tm1 = Getattr(tm,pname);
    if (!tm1) {
      tm1 = NewHash();
      Setattr(tm,NewString(pname),tm1);
      Delete(tm1);
    }
    tm = tm1;
  }

  /* Now see if this typemap op has been seen before */
  tm2 = Getattr(tm,tmop);
  if (!tm2) {
    tm2 = NewHash();
    Setattr(tm,tmop,tm2);
    Delete(tm2);
  }

  /* For a multi-valued typemap, the typemap code and information
     is really only stored in the last argument.  However, to
     make this work, we perform a really neat trick using
     the typemap operator name.

     For example, consider this typemap

       %typemap(in) (int foo, int *bar, char *blah[]) {
            ...
       }

     To store it, we look at typemaps for the following:

          operator                  type-name
          ----------------------------------------------
          "in"                      int foo
          "in-int+foo:"             int *bar
          "in-int+foo:-p.int+bar:   char *blah[]

     Notice how the operator expands to encode information about
     previous arguments.        

  */

  np = nextSibling(parms);
  if (np) {
    /* Make an entirely new operator key */
    String *newop = NewStringf("%s-%s+%s:",op,type,pname);
    /* Now reregister on the remaining arguments */
    Swig_typemap_register(newop,np,code,locals,kwargs);
    
    /*    Setattr(tm2,newop,newop); */
    Delete(newop);
  } else {
    Setattr(tm2,"code",NewString(code));
    Setattr(tm2,"type",Copy(type));
    Setattr(tm2,"typemap",NewStringf("typemap(%s) %s", op, SwigType_str(type,pname)));
    if (pname) {
      Setattr(tm2,"pname", NewString(pname));
    }
    Setattr(tm2,"locals", CopyParmList(locals));
    Setattr(tm2,"kwargs", CopyParmList(kwargs));
  }
}

/* -----------------------------------------------------------------------------
 * Swig_typemap_get()
 *
 * Retrieve typemap information from current scope.
 * ----------------------------------------------------------------------------- */

static Hash *
Swig_typemap_get(SwigType *type, String_or_char *name, int scope) {
  Hash *tm, *tm1;
  /* See if this type has been seen before */
  if ((scope < 0) || (scope > tm_scope)) return 0;
  tm = Getattr(typemaps[scope],type);
  if (!tm) {
    return 0;
  }
  if ((name) && Len(name)) {
    tm1 = Getattr(tm, name);
    return tm1;
  }
  return tm;
}

/* -----------------------------------------------------------------------------
 * Swig_typemap_copy()
 *
 * Copy a typemap
 * ----------------------------------------------------------------------------- */

int
Swig_typemap_copy(const String_or_char *op, ParmList *srcparms, ParmList *parms) {
  Hash *tm = 0;
  String *tmop;
  Parm *p;
  String *pname;
  SwigType *ptype;
  int ts = tm_scope;
  String *tmops, *newop;
  if (ParmList_len(parms) != ParmList_len(srcparms)) return -1;

  tmop = tmop_name(op);
  while (ts >= 0) {
    p = srcparms;
    tmops = NewString(tmop);
    while (p) {
      ptype = Getattr(p,"type");
      pname = Getattr(p,"name");
      
      /* Lookup the type */
      tm = Swig_typemap_get(ptype,pname,ts);
      if (!tm) break;
      
      tm = Getattr(tm,tmops);
      if (!tm) break;

      /* Got a match.  Look for next typemap */
      newop = NewStringf("%s-%s+%s:",tmops,ptype,pname);
      Delete(tmops);
      tmops = newop;
      p = nextSibling(p);
    }
    Delete(tmops);

    if (!p && tm) {

      /* Got some kind of match */
      Swig_typemap_register(op,parms, Getattr(tm,"code"), Getattr(tm,"locals"),Getattr(tm,"kwargs"));
      return 0;
    }
    ts--;
  }
  /* Not found */
  return -1;

}

/* -----------------------------------------------------------------------------
 * Swig_typemap_clear()
 *
 * Delete a multi-valued typemap
 * ----------------------------------------------------------------------------- */

void
Swig_typemap_clear(const String_or_char *op, ParmList *parms) {
  SwigType *type;
  String   *name;
  Parm     *p;
  String   *newop;
  Hash *tm = 0;

  /* This might not work */
  newop = NewString(op);
  p = parms;
  while (p) {
    type = Getattr(p,"type");
    name = Getattr(p,"name");
    tm = Swig_typemap_get(type,name,tm_scope);
    if (!tm) return;
    p = nextSibling(p);
    if (p) 
      Printf(newop,"-%s+%s:", type,name);
  }
  if (tm) {
    tm = Getattr(tm, tmop_name(newop));
    if (tm) {
      Delattr(tm,"code");
      Delattr(tm,"locals");
      Delattr(tm,"kwargs");
    }
  }
  Delete(newop);
}

/* -----------------------------------------------------------------------------
 * Swig_typemap_apply()
 *
 * Multi-argument %apply directive.  This is pretty horrible so I sure hope
 * it works.
 * ----------------------------------------------------------------------------- */

static
int count_args(String *s) {
  /* Count up number of arguments */
  int na = 0;
  char *c = Char(s);
  while (*c) {
    if (*c == '+') na++;
    c++;
  }
  return na;
}

int
Swig_typemap_apply(ParmList *src, ParmList *dest) {
  String *ssig, *dsig;
  Parm   *p, *np, *lastp, *dp, *lastdp = 0;
  int     narg = 0;
  int     ts = tm_scope;
  SwigType *type = 0, *name;
  Hash     *tm, *sm;
  int      match = 0;

  /*  Printf(stdout,"apply : %s --> %s\n", ParmList_str(src), ParmList_str(dest)); */

  /* Create type signature of source */
  ssig = NewString("");
  dsig = NewString("");
  p = src;
  dp = dest;
  lastp = 0;
  while (p) {
    lastp = p;
    lastdp = dp;
    np = nextSibling(p);
    if (np) {
      Printf(ssig,"-%s+%s:", Getattr(p,"type"), Getattr(p,"name"));
      Printf(dsig,"-%s+%s:", Getattr(dp,"type"), Getattr(dp,"name"));
      narg++;
    }
    p = np;
    dp = nextSibling(dp);
  }

  /* make sure a typemap node exists for the last destination node */
  tm = Getattr(typemaps[tm_scope],Getattr(lastdp,"type"));
  if (!tm) {
    tm = NewHash();
    Setattr(typemaps[tm_scope],Getattr(lastdp,"type"),tm);
    Delete(tm);
  }
  name = Getattr(lastdp,"name");
  if (name) {
    Hash *tm1 = Getattr(tm,name);
    if (!tm1) {
      tm1 = NewHash();
      Setattr(tm,NewString(name),tm1);
      Delete(tm1);
    }
    tm = tm1;
  }

  /* This is a little nasty.  We need to go searching for all possible typemaps in the
     source and apply them to the target */

  type = Getattr(lastp,"type");
  name = Getattr(lastp,"name");

  while (ts >= 0) {

    /* See if there is a matching typemap in this scope */
    sm = Swig_typemap_get(type,name,ts);

    if (sm) {
      /* Got a typemap.  Need to only merge attributes for methods that match our signature */
      Iterator ki;
      match = 1;
      for (ki = First(sm); ki.key; ki = Next(ki)) {
	/* Check for a signature match with the source signature */
	if ((count_args(ki.key) == narg) && (Strstr(ki.key,ssig))) {
	  String *oldm;
	  /* A typemap we have to copy */
	  String *nkey = Copy(ki.key);
	  Replace(nkey,ssig,dsig,DOH_REPLACE_ANY);

	  /* Make sure the typemap doesn't already exist in the target map */
	  
	  oldm = Getattr(tm,nkey);
	  if (!oldm || (!Getattr(tm,"code"))) {
	    String *code;
	    ParmList *locals;
	    ParmList *kwargs;
	    Hash *sm1 = ki.item;

	    code = Getattr(sm1,"code");
	    locals = Getattr(sm1,"locals");
	    kwargs = Getattr(sm1,"kwargs");
	    if (code) {
	      Replace(nkey,dsig,"", DOH_REPLACE_ANY);
	      Replace(nkey,"tmap:","", DOH_REPLACE_ANY);
	      Swig_typemap_register(nkey,dest,code,locals,kwargs);
	    }
	  }
	  Delete(nkey);
	}
      }
    }
    ts--;
  }
  return match;
}

/* -----------------------------------------------------------------------------
 * Swig_typemap_clear_apply()
 *
 * %clear directive.   Clears all typemaps for a type (in the current scope only).    
 * ----------------------------------------------------------------------------- */

/* Multi-argument %clear directive */
void
Swig_typemap_clear_apply(Parm *parms) {
  String *tsig;
  Parm   *p, *np, *lastp;
  int     narg = 0;
  Hash   *tm;
  String *name;

  /* Create a type signature of the parameters */
  tsig = NewString("");
  p = parms;
  lastp = 0;
  while (p) {
    lastp = p;
    np = nextSibling(p);
    if (np) {
      Printf(tsig,"-%s+%s:", Getattr(p,"type"), Getattr(p,"name"));
      narg++;
    }
    p = np;
  }
  tm = Getattr(typemaps[tm_scope],Getattr(lastp,"type"));
  if (!tm) {
    Delete(tsig);
    return;
  }
  name = Getattr(lastp,"name");
  if (name) {
    tm = Getattr(tm,name);
  }
  if (tm) {
    /* Clear typemaps that match our signature */
    Iterator ki, ki2;

    for (ki = First(tm); ki.key; ki = Next(ki)) {
      if (Strncmp(ki.key,"tmap:",5) == 0) {
	int na = count_args(ki.key);
	if ((na == narg) && Strstr(ki.key,tsig)) {
	  Hash *h = ki.item;
	  for (ki2 = First(h); ki2.key; ki2 = Next(ki2)) {
	    Delattr(h,ki2.key);
	  }
	}
      }
    }
  }
  Delete(tsig);
}

/* Internal function to strip array dimensions. */
static SwigType *strip_arrays(SwigType *type) {
  SwigType *t;
  int ndim;
  int i;
  t = Copy(type);
  ndim = SwigType_array_ndim(t);
  for (i = 0; i < ndim; i++) {
    SwigType_array_setdim(t,i,"ANY");
  }
  return t;
}

/* -----------------------------------------------------------------------------
 * Swig_typemap_search()
 *
 * Search for a typemap match.    Tries to find the most specific typemap
 * that includes a 'code' attribute.
 * ----------------------------------------------------------------------------- */

Hash *
Swig_typemap_search(const String_or_char *op, SwigType *type, String_or_char *name, SwigType **matchtype) {
  Hash *result = 0, *tm, *tm1, *tma;
  Hash *backup = 0;
  SwigType *noarrays = 0;
  SwigType *primitive = 0;
  SwigType *ctype = 0;
  int ts;
  int isarray;
  String *cname = 0;
  SwigType *unstripped = 0;
  String   *tmop = tmop_name(op);

  if ((name) && Len(name)) cname = name;
  ts = tm_scope;

  while (ts >= 0) {
    ctype = type;
    while (ctype) {
      /* Try to get an exact type-match */
      tm = Getattr(typemaps[ts],ctype);
      if (tm && cname) {
	tm1 = Getattr(tm,cname);
	if (tm1) {
	  result = Getattr(tm1,tmop);          /* See if there is a type-name match */
	  if (result && Getattr(result,"code")) goto ret_result;
	  if (result) backup = result;
	}
      }
      if (tm) {
	result = Getattr(tm,tmop);            /* See if there is simply a type match */
	if (result && Getattr(result,"code")) goto ret_result;
	if (result) backup = result;
      }
      isarray = SwigType_isarray(ctype);
      if (isarray) {
	/* If working with arrays, strip away all of the dimensions and replace with "ANY".
	   See if that generates a match */
	if (!noarrays) {
	  noarrays = strip_arrays(ctype);
	}
	tma = Getattr(typemaps[ts],noarrays);
	if (tma && cname) {
	  tm1 = Getattr(tma,cname);
	  if (tm1) {
	    result = Getattr(tm1,tmop);       /* type-name match */
	    if (result && Getattr(result,"code")) goto ret_result;
	    if (result) backup = result;
	  }
	}
	if (tma) {
	  result = Getattr(tma,tmop);        /* type match */
	  if (result && Getattr(result,"code")) goto ret_result;
	  if (result) backup = result;
	}
	Delete(noarrays);
	noarrays = 0;
      }
      
      /* No match so far.   If the type is unstripped, we'll strip its
         qualifiers and check.   Otherwise, we'll try to resolve a typedef */

      if (!unstripped) {
	unstripped = ctype;
	ctype = SwigType_strip_qualifiers(ctype);
	if (Strcmp(ctype,unstripped) != 0) continue;    /* Types are different */
	Delete(ctype);
	ctype = unstripped;
	unstripped = 0;
      }
      {
	String *octype;
	if (unstripped) {
 	  Delete(ctype);
	  ctype = unstripped;
	  unstripped = 0;
	}
	octype = ctype;
	ctype = SwigType_typedef_resolve(ctype);
	if (octype != type) Delete(octype);
      }
    }

    /* Hmmm. Well, no match seems to be found at all. See if there is some kind of default mapping */

    primitive = SwigType_default(type);
    while (primitive) {
      tm = Getattr(typemaps[ts],primitive);
      if (tm && cname) {
	tm1 = Getattr(tm,cname);
	if (tm1) {
	  result = Getattr(tm1,tmop);          /* See if there is a type-name match */
	  if (result) goto ret_result;
	}
      }
      if (tm) {			/* See if there is simply a type match */
	result = Getattr(tm,tmop);
	if (result) goto ret_result;
      }
      {
	SwigType *nprim = SwigType_default(primitive);
	Delete(primitive);
	primitive = nprim;
      }
    }
    if (ctype != type) { Delete(ctype); ctype = 0; }
    ts--;         /* Hmmm. Nothing found in this scope.  Guess we'll go try another scope */
  }
  result = backup;

 ret_result:
  if (noarrays) Delete(noarrays);
  if (primitive) Delete(primitive);
  if ((unstripped) && (unstripped != type)) Delete(unstripped);
  if (matchtype) {
    *matchtype = Copy(ctype);
  }
  if (type != ctype) Delete(ctype);
  return result;
}


/* -----------------------------------------------------------------------------
 * Swig_typemap_search_multi()
 *
 * Search for a multi-valued typemap.
 * ----------------------------------------------------------------------------- */

Hash *
Swig_typemap_search_multi(const String_or_char *op, ParmList *parms, int *nmatch) {
  SwigType *type;
  SwigType *mtype = 0;
  String   *name;
  String   *newop;
  Hash     *tm, *tm1;

  if (!parms) {
    *nmatch = 0;
    return 0;
  }
  type = Getattr(parms,"type");
  name = Getattr(parms,"name");

  /* Try to find a match on the first type */
  tm = Swig_typemap_search(op, type, name, &mtype);
  if (tm) {
    if (mtype && SwigType_isarray(mtype)) {
      Setattr(parms,"tmap:match", mtype);
    }
    Delete(mtype);
    newop = NewStringf("%s-%s+%s:", op, type,name);
    tm1 = Swig_typemap_search_multi(newop, nextSibling(parms), nmatch);
    if (tm1) tm = tm1;
    if (Getattr(tm,"code")) {
      *(nmatch) = *nmatch + 1;
    } else {
      tm = 0;
    }
    Delete(newop);
  }
  return tm;
}


/* -----------------------------------------------------------------------------
 * typemap_replace_vars()
 *
 * Replaces typemap variables on a string.  index is the $n variable.
 * type and pname are the type and parameter name.
 * ----------------------------------------------------------------------------- */

static
void replace_local_types(ParmList *p, const String *name, const String *rep) {
  SwigType *t;
  while (p) {
    t = Getattr(p,"type");
    Replace(t,name,rep,DOH_REPLACE_ANY);
    p = nextSibling(p);
  }
}

static
int check_locals(ParmList *p, const char *s) {
  while (p) {
    char *c = GetChar(p,"type");
    if (strstr(c,s)) return 1;
    p = nextSibling(p);
  }
  return 0;
}

static
void typemap_replace_vars(String *s, ParmList *locals, SwigType *type, String *pname, String *lname, int index) 
{
  char var[512];
  char *varname;
  SwigType *ftype;

  Replaceall(s,"$typemap","$TYPEMAP");

  ftype = SwigType_typedef_resolve_all(type);

  if (!pname) pname = lname;
  {
    Parm *p;
    int  rep = 0;
    p = locals;
    while (p) {
      if (Strchr(Getattr(p,"type"),'$')) rep = 1;
      p = nextSibling(p);
    }
    if (!rep) locals = 0;
  }
  
  sprintf(var,"$%d_",index);
  varname = &var[strlen(var)];
    
  /* If the original datatype was an array. We're going to go through and substitute
     its array dimensions */
    
  if (SwigType_isarray(type)) {
    String *size;
    int  ndim = SwigType_array_ndim(type);
    int i;
    size = NewString("");
    for (i = 0; i < ndim; i++) {
      String *dim = SwigType_array_getdim(type,i);
      if (index == 1) {
	char t[32];
	sprintf(t,"$dim%d",i);
	Replace(s,t,dim,DOH_REPLACE_ANY);
	replace_local_types(locals,t,dim);	
      }
      sprintf(varname,"dim%d",i);
      Replace(s,var,dim,DOH_REPLACE_ANY);
      replace_local_types(locals,var,dim);	
      if (Len(size)) Putc('*',size);
      Append(size,dim);
      Delete(dim);
    }
    sprintf(varname,"size");
    Replace(s,var,size,DOH_REPLACE_ANY);
    replace_local_types(locals,var,size);	    
    Delete(size);
  }

  /* Parameter name substitution */
  if (index == 1) {
    Replace(s,"$parmname",pname, DOH_REPLACE_ANY);
  }
  strcpy(varname,"name");
  Replace(s,var,pname,DOH_REPLACE_ANY);

  /* Type-related stuff */
  {
    SwigType *star_type, *amp_type, *base_type;
    SwigType *ltype, *star_ltype, *amp_ltype;
    String *mangle, *star_mangle, *amp_mangle, *base_mangle, *base_name;
    String *descriptor, *star_descriptor, *amp_descriptor;
    String *ts;
    char   *sc;

    sc = Char(s);

    if (strstr(sc,"type") || check_locals(locals,"type")) {
      /* Given type : $type */
      ts = SwigType_str(type,0);
      if (index == 1) {
	Replace(s, "$type", ts, DOH_REPLACE_ANY);
	replace_local_types(locals,"$type",type);
      }
      strcpy(varname,"type");
      Replace(s,var,ts,DOH_REPLACE_ANY);
      replace_local_types(locals,var,type);
      Delete(ts);
      sc = Char(s);
    }
    if (strstr(sc,"ltype") || check_locals(locals,"ltype")) {
      /* Local type:  $ltype */
      ltype = SwigType_ltype(type);
      ts = SwigType_str(ltype,0);
      if (index == 1) {
	Replace(s, "$ltype", ts, DOH_REPLACE_ANY);
	replace_local_types(locals,"$ltype",ltype);
      }
      strcpy(varname,"ltype");
      Replace(s,var,ts,DOH_REPLACE_ANY);
      replace_local_types(locals,var,ltype);
      Delete(ts);
      Delete(ltype);
      sc = Char(s);
    }
    if (strstr(sc,"mangle") || strstr(sc,"descriptor")) {
      /* Mangled type */
      
      mangle = SwigType_manglestr(type);
      if (index == 1)
	Replace(s, "$mangle", mangle, DOH_REPLACE_ANY);
      strcpy(varname,"mangle");
      Replace(s,var,mangle,DOH_REPLACE_ANY);
    
      descriptor = NewStringf("SWIGTYPE%s", mangle);

      if (index == 1)
	if (Replace(s, "$descriptor", descriptor, DOH_REPLACE_ANY))
	  SwigType_remember(type);
      
      strcpy(varname,"descriptor");
      if (Replace(s,var,descriptor,DOH_REPLACE_ANY))
	SwigType_remember(type);
      
      Delete(descriptor);
      Delete(mangle);
    }
    
    /* One pointer level removed */
    /* This creates variables of the form
          $*n_type
          $*n_ltype
    */

    if (SwigType_ispointer(ftype) || (SwigType_isarray(ftype)) || (SwigType_isreference(ftype))) {
      if (!(SwigType_isarray(type) || SwigType_ispointer(type) || SwigType_isreference(type))) {
	star_type = Copy(ftype);
      } else {
	star_type = Copy(type);
      }
      if (!SwigType_isreference(star_type)) {
	if (SwigType_isarray(star_type)) {
	  SwigType_del_element(star_type);
	} else {
	  SwigType_del_pointer(star_type);
	}
	ts = SwigType_str(star_type,0);
	if (index == 1) {
	  Replace(s, "$*type", ts, DOH_REPLACE_ANY);
	  replace_local_types(locals,"$*type",star_type);
	}
	sprintf(varname,"$*%d_type",index);
	Replace(s,varname,ts,DOH_REPLACE_ANY);
	replace_local_types(locals,varname,star_type);
	Delete(ts);
      } else {
	SwigType_del_element(star_type);
      }
      star_ltype = SwigType_ltype(star_type);
      ts = SwigType_str(star_ltype,0);
      if (index == 1) {
	Replace(s, "$*ltype", ts, DOH_REPLACE_ANY);
	replace_local_types(locals,"$*ltype",star_ltype);
      }
      sprintf(varname,"$*%d_ltype",index);
      Replace(s,varname,ts,DOH_REPLACE_ANY);
      replace_local_types(locals,varname,star_ltype);
      Delete(ts);
      Delete(star_ltype);
      
      star_mangle = SwigType_manglestr(star_type);
      if (index == 1) 
	Replace(s, "$*mangle", star_mangle, DOH_REPLACE_ANY);
      
      sprintf(varname,"$*%d_mangle",index);
      Replace(s,varname,star_mangle,DOH_REPLACE_ANY);
      
      star_descriptor = NewStringf("SWIGTYPE%s", star_mangle);
      if (index == 1)
	if (Replace(s, "$*descriptor",
		    star_descriptor, DOH_REPLACE_ANY))
	  SwigType_remember(star_type);
      sprintf(varname,"$*%d_descriptor",index);
      if (Replace(s,varname,star_descriptor,DOH_REPLACE_ANY))
	SwigType_remember(star_type);
      
      Delete(star_descriptor);
      Delete(star_mangle);
      Delete(star_type);
    }
    else {
      /* TODO: Signal error if one of the $* substitutions is
	 requested */
    }
    /* One pointer level added */
    amp_type = Copy(type);
    SwigType_add_pointer(amp_type);
    ts = SwigType_str(amp_type,0);
    if (index == 1) {
      Replace(s, "$&type", ts, DOH_REPLACE_ANY);
      replace_local_types(locals,"$&type",amp_type);
    }
    sprintf(varname,"$&%d_type",index);
    Replace(s,varname,ts,DOH_REPLACE_ANY);
    replace_local_types(locals,varname,amp_type);
    Delete(ts);
    
    amp_ltype = SwigType_ltype(type);
    SwigType_add_pointer(amp_ltype);
    ts = SwigType_str(amp_ltype,0);
    
    if (index == 1) {
      Replace(s, "$&ltype", ts, DOH_REPLACE_ANY);
      replace_local_types(locals, "$&ltype", amp_ltype);
    }
    sprintf(varname,"$&%d_ltype",index);
    Replace(s,varname,ts,DOH_REPLACE_ANY);
    replace_local_types(locals,varname,amp_ltype);
    Delete(ts);
    Delete(amp_ltype);
    
    amp_mangle = SwigType_manglestr(amp_type);
    if (index == 1) 
      Replace(s, "$&mangle", amp_mangle, DOH_REPLACE_ANY);
    sprintf(varname,"$&%d_mangle",index);
    Replace(s,varname,amp_mangle,DOH_REPLACE_ANY);
    
    amp_descriptor = NewStringf("SWIGTYPE%s", amp_mangle);
    if (index == 1) 
      if (Replace(s, "$&descriptor",
		  amp_descriptor, DOH_REPLACE_ANY))
	SwigType_remember(amp_type);
    sprintf(varname,"$&%d_descriptor",index);
    if (Replace(s,varname,amp_descriptor,DOH_REPLACE_ANY))
      SwigType_remember(amp_type);
    
    Delete(amp_descriptor);
    Delete(amp_mangle);
    Delete(amp_type);

    /* Base type */
    if (SwigType_isarray(type)) {
      SwigType *bt = Copy(type);
      Delete(SwigType_pop_arrays(bt));
      base_type = SwigType_str(bt,0);
      Delete(bt);
    } else {
      base_type = SwigType_base(type);
    }

    base_name = SwigType_namestr(base_type);
    if (index == 1) {
      Replace(s,"$basetype", base_name, DOH_REPLACE_ANY);
      replace_local_types(locals,"$basetype", base_name);
    }
    strcpy(varname,"basetype");
    Replace(s,var,base_type,DOH_REPLACE_ANY);
    replace_local_types(locals,var,base_name);
    
    base_mangle = SwigType_manglestr(base_type);
    if (index == 1)
      Replace(s,"$basemangle", base_mangle, DOH_REPLACE_ANY);
    strcpy(varname,"basemangle");
    Replace(s,var,base_mangle,DOH_REPLACE_ANY);
    Delete(base_mangle);
    Delete(base_type);
    Delete(base_name);
  }
  
  /* Replace any $n. with (&n)-> */
  {
    char temp[64];
    sprintf(var,"$%d.",index);
    sprintf(temp,"(&$%d)->", index);
    Replace(s,var,temp,DOH_REPLACE_ANY);
  }

  /* Replace the bare $n variable */
  sprintf(var,"$%d",index);
  Replace(s,var,lname,DOH_REPLACE_ANY);
  Delete(ftype);
}

/* ------------------------------------------------------------------------
 * static typemap_locals()
 *
 * Takes a string, a parameter list and a wrapper function argument and
 * creates the local variables.
 * ------------------------------------------------------------------------ */

static void typemap_locals(DOHString *s, ParmList *l, Wrapper *f, int argnum) {
  Parm *p;
  char *new_name;

  p = l;
  while (p) {
    SwigType *pt = Getattr(p,"type");
    String   *pn = Getattr(p,"name");
    String   *value = Getattr(p,"value");
    if (pn) {
      if (Len(pn) > 0) {
	String *str;
	int     isglobal = 0;

	str = NewString("");

	if (Strncmp(pn,"_global_",8) == 0) {
	    isglobal = 1;
	}

	/* If the user gave us $type as the name of the local variable, we'll use
	   the passed datatype instead */

	if ((argnum >= 0) && (!isglobal)) {
	  Printf(str,"%s%d",pn,argnum);
	} else {
	  Printf(str,"%s",pn);
	}
	if (isglobal && Wrapper_check_local(f,str)) {
	    p = nextSibling(p);
	    continue;
	}
	if (value) {
	    new_name = Wrapper_new_localv(f,str, SwigType_str(pt,str), "=", value, NIL);
	} else {
	    new_name = Wrapper_new_localv(f,str, SwigType_str(pt,str), NIL);
	}
	if (!isglobal) {
	    /* Substitute  */
	    Replace(s,pn,new_name,DOH_REPLACE_ID | DOH_REPLACE_NOQUOTE);
	}
      }
    }
    p = nextSibling(p);
  }
}

/* -----------------------------------------------------------------------------
 * Swig_typemap_lookup()
 *
 * Perform a typemap lookup (ala SWIG1.1)
 * ----------------------------------------------------------------------------- */

String *Swig_typemap_lookup(const String_or_char *op, SwigType *type, String_or_char *pname,
			  String_or_char *lname, String_or_char *source,
			  String_or_char *target, Wrapper *f) 
{
  Hash   *tm;
  String *s = 0;
  SwigType *mtype = 0;
  ParmList *locals;
  tm = Swig_typemap_search(op,type,pname,&mtype);
  if (!tm) return 0;

  s = Getattr(tm,"code");
  if (!s) return 0;

  /* Blocked */
  if (Cmp(s,"pass") == 0) return 0;

  s = Copy(s);             /* Make a local copy of the typemap code */

  locals = Getattr(tm,"locals");
  if (locals) locals = CopyParmList(locals);

  /* This is wrong.  It replaces locals in place.   Need to fix this */
  if (mtype && SwigType_isarray(mtype)) {
    typemap_replace_vars(s,locals,mtype,pname,lname,1);
  } else {
    typemap_replace_vars(s,locals,type,pname,lname,1);
  }

  if (locals && f) {
    typemap_locals(s,locals,f,-1);
  }

  replace_embedded_typemap(s);   

  /* Now perform character replacements */
  Replace(s,"$source",source,DOH_REPLACE_ANY);
  Replace(s,"$target",target,DOH_REPLACE_ANY);

  /*  {
    String *tmname = Getattr(tm,"typemap");
    if (tmname) Replace(s,"$typemap",tmname, DOH_REPLACE_ANY);
  }
  */

  Replace(s,"$parmname",pname, DOH_REPLACE_ANY);
  /*  Replace(s,"$name",pname,DOH_REPLACE_ANY); */

  Delete(locals);
  Delete(mtype);
  return s;
}

/* -----------------------------------------------------------------------------
 * Swig_typemap_lookup_new()
 *
 * Attach one or more typemaps to a node
 * ----------------------------------------------------------------------------- */

String *Swig_typemap_lookup_new(const String_or_char *op, Node *node, const String_or_char *lname, Wrapper *f)
{
  SwigType *type;
  SwigType *mtype = 0;
  String   *pname;
  Hash     *tm;
  String   *s = 0;
  String   *sdef = 0;
  ParmList *locals;
  ParmList *kw;
  char     temp[256];
  String   *symname;
  String   *cname = 0;
  String   *clname = 0;

  /* special case, we need to check for 'ref' call 
     and set the defaul code 'sdef' */
  if (Cmp(op,"newfree") == 0) { 
    sdef = Swig_ref_call(node, lname);
  }

  type  = Getattr(node,"type");
  if (!type) return sdef;

  pname = Getattr(node,"name");
  tm = Swig_typemap_search(op,type,pname,&mtype);
  if (!tm) return sdef;

  s = Getattr(tm,"code");
  if (!s) return sdef;

  /* Empty typemap. No match */
  if (Cmp(s,"pass") == 0) return sdef;

  s = Copy(s);             /* Make a local copy of the typemap code */

  locals = Getattr(tm,"locals");
  if (locals) locals = CopyParmList(locals);

  if (pname) {
    if (SwigType_istemplate(pname)) {
      cname = SwigType_namestr(pname);
      pname = cname;
    }
  }
  if (SwigType_istemplate((char*)lname)) {
    clname = SwigType_namestr((char *)lname);
    lname = clname;
  }

  if (mtype && SwigType_isarray(mtype)) {
    typemap_replace_vars(s,locals,mtype,pname,(char *) lname,1);
  } else {
    typemap_replace_vars(s,locals,type,pname,(char *) lname,1);
  }

  if (locals && f) {
    typemap_locals(s,locals,f,-1);
  }
  replace_embedded_typemap(s);
  /*  {
    String *tmname = Getattr(tm,"typemap");
    if (tmname) Replace(s,"$typemap",tmname, DOH_REPLACE_ANY);
    }*/

  Replace(s,"$name",pname,DOH_REPLACE_ANY);
  
  symname = Getattr(node,"sym:name");
  if (symname) {
    Replace(s,"$symname",symname, DOH_REPLACE_ANY);
  }

  Setattr(node,tmop_name(op),s);
  if (locals) {
    sprintf(temp,"%s:locals", Char(op));
    Setattr(node,tmop_name(temp), locals);
    Delete(locals);  
  }

  if (checkAttribute(tm,"type","SWIGTYPE")) {
    sprintf(temp,"%s:SWIGTYPE", Char(op));
    Setattr(node,tmop_name(temp),"1");
  }

  /* Attach kwargs */
  kw = Getattr(tm,"kwargs");
  while (kw) {
    sprintf(temp,"%s:%s",Char(op),Char(Getattr(kw,"name")));
    Setattr(node,tmop_name(temp), Copy(Getattr(kw,"value")));
    kw = nextSibling(kw);
  }

  /* Look for warnings */
  {
    String *w;
    sprintf(temp,"%s:warning", Char(op));
    w = Getattr(node,tmop_name(temp));
    if (w) {
      Swig_warning(0,Getfile(node),Getline(node),"%s\n", w);
    }
  }

  /* Look for code fragments */
  {
    String *f;
    sprintf(temp,"%s:fragment", Char(op));
    f = Getattr(node,tmop_name(temp));
    if (f) {
      char  *c, *tok;
      String *t = Copy(f);
      c = Char(t);
      tok = strtok(c,",");
      while (tok) {
	Swig_fragment_emit(tok);
	tok = strtok(NULL,",");
      }
      Delete(t);
    }
  }
    
  if (cname) Delete(cname);
  if (clname) Delete(clname);
  if (mtype) Delete(mtype);
  if (sdef) { /* put 'ref' and 'newfree' codes together */
    String *p = NewStringf("%s\n%s", sdef, s);
    Delete(s);
    Delete(sdef);
    s = p;
  }
  return s;
}

/* -----------------------------------------------------------------------------
 * Swig_typemap_attach_kwargs()
 *
 * If this hash (tm) contains a linked list of parameters under its "kwargs"
 * attribute, add keys for each of those named keyword arguments to this
 * parameter for later use.
 * For example, attach the typemap attributes to p:
 * %typemap(in, foo="xyz") ...
 * A new attribute called "tmap:in:foo" with value "xyz" is attached to p.
 * ----------------------------------------------------------------------------- */

void
Swig_typemap_attach_kwargs(Hash *tm, const String_or_char *op, Parm *p) {
  String *temp = NewString("");
  Parm *kw = Getattr(tm,"kwargs");
  while (kw) {
    Clear(temp);
    Printf(temp,"%s:%s",op,Getattr(kw,"name"));
    Setattr(p,tmop_name(temp),Copy(Getattr(kw,"value")));
    kw = nextSibling(kw);
  }
  Delete(temp);
}

/* -----------------------------------------------------------------------------
 * Swig_typemap_warn()
 *
 * If any warning message is attached to this parameter's "tmap:op:warning"
 * attribute, print that warning message.
 * ----------------------------------------------------------------------------- */

static void
Swig_typemap_warn(const String_or_char *op, Parm *p) {
  String *temp = NewStringf("%s:warning",op);
  String *w = Getattr(p,tmop_name(temp));
  Delete(temp);
  if (w) {
    Swig_warning(0,Getfile(p),Getline(p),"%s\n",w);
  }
}

static void
Swig_typemap_emit_code_fragments(const String_or_char *op, Parm *p) {
  String *temp = NewStringf("%s:fragment",op);
  String *f = Getattr(p,tmop_name(temp));
  if (f) {
    char  *c, *tok;
    String *t = Copy(f);
    c = Char(t);
    tok = strtok(c,",");
    while (tok) {
      Swig_fragment_emit(tok);
      tok = strtok(NULL,",");
    }
    Delete(t);
  }
  Delete(temp);
}

/* -----------------------------------------------------------------------------
 * Swig_typemap_attach_parms()
 *
 * Given a parameter list, this function attaches all of the typemaps for a
 * given typemap type
 * ----------------------------------------------------------------------------- */

void
Swig_typemap_attach_parms(const String_or_char *op, ParmList *parms, Wrapper *f) {
  Parm *p, *firstp;
  Hash *tm;
  int   nmatch = 0;
  int   i;
  String *s;
  ParmList *locals;
  int   argnum = 0;
  char  temp[256];

  p = parms;
  while (p) {
    argnum++;
    nmatch = 0;
    tm = Swig_typemap_search_multi(op,p,&nmatch);
    if (!tm) {
      p = nextSibling(p);
      continue;
    }
    s = Getattr(tm,"code");
    if (!s) {
      p = nextSibling(p);
      continue;
    }

    /* Empty typemap. No match */
    if (Cmp(s,"pass") == 0) {
      p = nextSibling(p);
      continue;
    }

    s = Copy(s);
    locals = Getattr(tm,"locals");
    if (locals) locals = CopyParmList(locals);
    firstp = p;
    for (i = 0; i < nmatch; i++) {
      SwigType *type;
      String   *pname;
      String   *lname;
      SwigType *mtype;

      type = Getattr(p,"type");
      pname = Getattr(p,"name");
      lname = Getattr(p,"lname");
      mtype = Getattr(p,"tmap:match");

      if (mtype) {
	typemap_replace_vars(s,locals, mtype,pname,lname,i+1);
	Delattr(p,"tmap:match");
      } else {
	typemap_replace_vars(s,locals, type,pname,lname,i+1);
      }

      if (checkAttribute(tm,"type","SWIGTYPE")) {
	sprintf(temp,"%s:SWIGTYPE", Char(op));
	Setattr(p,tmop_name(temp),"1");
      }
      p = nextSibling(p);
    }
    
    if (locals && f) {
      typemap_locals(s,locals,f,argnum);
    }

    replace_embedded_typemap(s);   

    /* Replace the argument number */
    sprintf(temp,"%d",argnum);
    Replace(s,"$argnum",temp, DOH_REPLACE_ANY);

    /* Attach attributes to object */
    Setattr(firstp,tmop_name(op),s);           /* Code object */

    if (locals) {
      sprintf(temp,"%s:locals", Char(op));
      Setattr(firstp,tmop_name(temp), locals);
      Delete(locals);
    }

    /* Attach a link to the next parameter.  Needed for multimaps */
    sprintf(temp,"%s:next",Char(op));
    Setattr(firstp,tmop_name(temp),p);

    /* Attach kwargs */
    Swig_typemap_attach_kwargs(tm,op,firstp);
    
    /* Print warnings, if any */
    Swig_typemap_warn(op,firstp);

    /* Look for code fragments */
    Swig_typemap_emit_code_fragments(op,firstp);
  }
}

/* -----------------------------------------------------------------------------
 * split_embedded()
 *
 * This function replaces the special variable $typemap(....) with typemap
 * code.  The general form of $typemap is as follows:
 *
 *   $TYPEMAP(method, $var1=value, $var2=value, $var3=value,...)
 *
 * For example:
 *
 *   $TYPEMAP(in, $1=int x, $input=y, ...)
 *
 * ----------------------------------------------------------------------------- */

/* Splits the arguments of an embedded typemap */
static List *split_embedded(String *s) {
  List *args = 0;
  char *c,*start;
  int  level=0;
  int  leading = 1;
  args = NewList();
  
  c = Strstr(s,"(");
  c++;

  start = c;
  while (*c) {
    if (*c == '\"') {
      c++;
      while (*c) {
	if (*c == '\\') {
	  c++;
	} else {
	  if (*c == '\"') break;
	}
	c++;
      }
    }
    if ((level == 0) && ((*c == ',') || (*c == ')'))) {
      String *tmp = NewStringWithSize(start,c-start);
      Append(args,tmp);
      Delete(tmp);
      start = c+1;
      leading = 1;
      if (*c == ')') break;
      c++;
      continue;
    }
    if (*c == '(') level++;
    if (*c == ')') level--;
    if (isspace((int)*c) && leading) start++; 
    if (!isspace((int)*c)) leading = 0;
    c++;
  }
  return args;
}

static void split_var(String *s, String **name, String **value) {
  char *eq;
  char *c;

  eq = Strstr(s,"=");
  if (!eq) {
    *name = 0;
    *value = 0;
    return;
  }
  c = Char(s);
  *name = NewStringWithSize(c,eq-c);
  
  /* Look for $n variables */
  if (isdigit((int)*(c))) {
    /* Parse the value as a type */
    String *v;
    Parm *p;
    v = NewString(eq+1);
    p = Swig_cparse_parm(v);
    Delete(v);
    *value = p;
  } else {
    *value = NewString(eq+1);
  }
}

static void replace_embedded_typemap(String *s) {
  while (Strstr(s,"$TYPEMAP(")) {

    /* Gather the argument */
    char *start, *end=0,*c;
    int  level = 0;
    String *tmp;
    start = Strstr(s,"$TYPEMAP(");
    c = start;
    while (*c) {
      if (*c == '(') level++;
      if (*c == ')') {
	level--;
	if (level == 0) {
	  end = c+1;
	  break;
	}
      }
      c++;
    }
    if (end) {
      tmp = NewStringWithSize(start,(end-start));
    } else {
      tmp = 0;
    }

    /* Got a substitution. Split it apart into pieces */
    if (tmp) {
      List   *l;
      Hash   *vars;
      String *method;
      int i;

      l = split_embedded(tmp);
      vars = NewHash();
      for (i = 1; i < Len(l); i++) {
	String *n, *v;
	split_var(Getitem(l,i),&n,&v);
	if (n && v) {
	  Insert(n,0,"$");
	  Setattr(vars,n,v);
	}
      }

      method = Getitem(l,0);
      /* Generate the parameter list for matching typemaps */

      {
	Parm *p = 0;
	Parm *first = 0;
	char  temp[32];
	int   n = 1;
	while (1) {
	  Hash *v;
	  sprintf(temp,"$%d",n);
	  v = Getattr(vars,temp);
	  if (v) {
	    if (p) {
	      set_nextSibling(p,v);
	      set_previousSibling(v,p);
	    }
	    p = v;
	    Setattr(p,"lname",Getattr(p,"name"));
	    if (Getattr(p,"value")) {
	      Setattr(p,"name",Getattr(p,"value"));
	    }
	    if (!first) first = p;
	    DohIncref(p);
	    Delattr(vars,temp);
	  } else {
	    break;
	  }
	  n++;
	}
	/* Perform a typemap search */
	if (first) {
	  Swig_typemap_attach_parms(method,first,0);
	  {
	    String *tm;
	    int     match = 0;
	    char attr[64];
	    sprintf(attr,"tmap:%s",Char(method));
	    
	    /* Look for the typemap code */
	    tm = Getattr(first,attr);
	    if (tm) {
	      sprintf(attr,"tmap:%s:next",Char(method));
	      if (!Getattr(first,attr)) {
		/* Should be no more matches.  Hack??? */
		/* Replace all of the remaining variables */
		Iterator ki;
		for (ki = First(vars); ki.key; ki = Next(ki)) {
		  Replace(tm,ki.key,ki.item, DOH_REPLACE_ANY);
		}
		/* Do the replacement */
		Replace(s,tmp,tm, DOH_REPLACE_ANY);
		Delete(tm);
		Delete(vars);
		match = 1;
	      }
	    }
	    if (!match) {
	      Swig_error(Getfile(s),Getline(s),"No typemap found for %s\n", tmp);
	    }
	  }
	}
      }
      Replace(s,tmp,"<embedded typemap>", DOH_REPLACE_ANY);
    }
  }
}

/* -----------------------------------------------------------------------------
 * Swig_typemap_debug()
 * ----------------------------------------------------------------------------- */

void Swig_typemap_debug() {
  int ts;
  Printf(stdout,"---[ typemaps ]--------------------------------------------------------------\n");
  
  ts = tm_scope;
  while (ts >= 0) {
    Printf(stdout,"::: scope %d\n\n",ts);
    Printf(stdout,"%s\n", typemaps[ts]);
    ts--;
  }
  Printf(stdout,"-----------------------------------------------------------------------------\n");
}


