/* -----------------------------------------------------------------------------
 * This file is part of SWIG, which is licensed as a whole under version 3
 * (or any later version) of the GNU General Public License. Some additional
 * terms also apply to certain portions of SWIG. The full details of the SWIG
 * license and copyrights can be found in the LICENSE and COPYRIGHT files
 * included with the SWIG source code as distributed by the SWIG developers
 * and at https://www.swig.org/legal.html.
 *
 * typemap.c
 *
 * A somewhat generalized implementation of SWIG1.1 typemaps.
 * ----------------------------------------------------------------------------- */

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

#if 0
#define SWIG_DEBUG
#endif

static int typemap_search_debug = 0;
static int typemaps_used_debug = 0;
static int typemap_register_debug = 0;
static int in_typemap_search_multi = 0;

static void replace_embedded_typemap(String *s, ParmList *parm_sublist, Wrapper *f, Node *file_line_node);

/* -----------------------------------------------------------------------------
 * 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 typemap method has the following attributes:
 *
 *    "type"    -  Typemap type
 *    "pname"   -  Parameter name
 *    "code"    -  Typemap code
 *    "source"  -  Source directive (%apply or %typemap) for the typemap
 *    "locals"  -  Local variables (if any)
 *    "kwargs"  -  Typemap attributes
 *
 * Example for a typemap method named "in":
 *   %typemap(in, warning="987:my warning", noblock=1) int &my_int (int tmp) "$1 = $input;"
 *
 *    "type"    -  r.int
 *    "pname"   -  my_int
 *    "code"    -  $1 = $input;
 *    "source"  -  typemap(in) int &my_int
 *    "locals"  -  int tmp
 *    "kwargs"  -  warning="987:my typemap warning", foo=123
 *
 * ----------------------------------------------------------------------------- */

static Hash *typemaps;

/* -----------------------------------------------------------------------------
 * typemap_identifier_fix()
 *
 * Create a type that can be used as a hash key lookup independent of the various
 * ways a template parameter list can be defined. This is achieved by fully
 * resolving the template parameters.
 *
 * This is a copy and modification of feature_identifier_fix in parser.y.
 * ----------------------------------------------------------------------------- */

static SwigType *typemap_identifier_fix(const SwigType *s) {
  String *tp = SwigType_istemplate_templateprefix(s);
  if (tp) {
    String *ts, *ta, *tq, *tr;
    ts = SwigType_templatesuffix(s);
    ta = SwigType_templateargs(s);
    tq = Swig_symbol_type_qualify(ta, 0);
    tr = SwigType_typedef_resolve_all(ta);
    Append(tp, tr);
    Append(tp, ts);
    Delete(ts);
    Delete(ta);
    Delete(tq);
    Delete(tr);
    return tp;
  } else {
    return NewString(s);
  }
}

static Hash *get_typemap(const SwigType *type) {
  Hash *tm = 0;
  SwigType *dtype = 0;
  SwigType *hashtype;

  if (SwigType_istemplate(type)) {
    SwigType *rty = typemap_identifier_fix(type);
    String *ty = Swig_symbol_template_deftype(rty, 0);
    dtype = Swig_symbol_type_qualify(ty, 0);
    type = dtype;
    Delete(ty);
    Delete(rty);
  }

  /* remove unary scope operator (::) prefix indicating global scope for looking up in the hashmap */
  hashtype = SwigType_remove_global_scope_prefix(type);
  tm = Getattr(typemaps, hashtype);

  Delete(dtype);
  Delete(hashtype);

  return tm;
}

static void set_typemap(const SwigType *type, Hash **tmhash) {
  SwigType *hashtype = 0;
  Hash *new_tm = 0;
  assert(*tmhash == 0);
  if (SwigType_istemplate(type)) {
    SwigType *rty = typemap_identifier_fix(type);
    String *ty = Swig_symbol_template_deftype(rty, 0);
    String *tyq = Swig_symbol_type_qualify(ty, 0);
    hashtype = SwigType_remove_global_scope_prefix(tyq);
    *tmhash = Getattr(typemaps, hashtype);
    Delete(rty);
    Delete(tyq);
    Delete(ty);
  } else {
    hashtype = SwigType_remove_global_scope_prefix(type);
  }

  if (!*tmhash) {
    /* this type has not been seen before even after resolving template parameter types */
    new_tm = NewHash();
    *tmhash = new_tm;
  }

  /* note that the unary scope operator (::) prefix indicating global scope has been removed from the type */
  Setattr(typemaps, hashtype, *tmhash);

  Delete(hashtype);
  Delete(new_tm);
}

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

void Swig_typemap_init(void) {
  typemaps = NewHash();
}

static String *typemap_method_name(const_String_or_char_ptr tmap_method) {
  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.

     (typemap_attach_kwargs calls typemap_method_name several times with
     the "same" String *tmap_method (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 *method_without_object_identity = Char(tmap_method);
  if (!names)
    names = NewHash();
  s = Getattr(names, method_without_object_identity);
  if (s)
    return s;
  s = NewStringf("tmap:%s", tmap_method);
  Setattr(names, method_without_object_identity, s);
  Delete(s);
  return s;
}

/* -----------------------------------------------------------------------------
 * typemap_register()
 *
 * Internal implementation for Swig_typemap_register()
 * ----------------------------------------------------------------------------- */

static void typemap_register(const_String_or_char_ptr tmap_method, ParmList *parms, const_String_or_char_ptr code, ParmList *locals, ParmList *kwargs,
                             String *source_directive) {
  Hash *tm;
  Hash *tm1;
  Hash *tm2;
  Parm *np;
  String *tm_method;
  SwigType *type;
  String *pname;
  if (!parms)
    return;

  if (typemap_register_debug) {
    Printf(stdout, "Registering - %s\n", tmap_method);
    Swig_print_node(parms);
  }

  tm_method = typemap_method_name(tmap_method);

  /* 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 = get_typemap(type);
  if (!tm) {
    set_typemap(type, &tm);
  }
  if (pname) {
    /* See if parameter has been seen before */
    tm1 = Getattr(tm, pname);
    if (!tm1) {
      tm1 = NewHash();
      Setattr(tm, pname, tm1);
      Delete(tm1);
    }
    tm = tm1;
  }

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

  /* For a multi-argument 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 method name.

     For example, consider this typemap

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

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

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

     Notice how the typemap method name expands to encode information about
     previous arguments.

   */

  np = nextSibling(parms);
  if (np) {
    /* Make an entirely new typemap method key */
    String *multi_tmap_method = NewStringf("%s-%s+%s:", tmap_method, type, pname);

    /* Now reregister on the remaining arguments */
    typemap_register(multi_tmap_method, np, code, locals, kwargs, source_directive);

    Delete(multi_tmap_method);
  } else {
    ParmList *clocals = CopyParmList(locals);
    ParmList *ckwargs = CopyParmList(kwargs);

    Setfile(tm2, Getfile(code));
    Setline(tm2, Getline(code));
    Setattr(tm2, "code", code);
    Setattr(tm2, "type", type);
    Setattr(tm2, "source", source_directive);
    if (pname) {
      Setattr(tm2, "pname", pname);
    }
    Setattr(tm2, "locals", clocals);
    Setattr(tm2, "kwargs", ckwargs);

    Delete(clocals);
    Delete(ckwargs);
  }
}

/* -----------------------------------------------------------------------------
 * Swig_typemap_register()
 *
 * Add a new, possibly multi-argument, typemap
 * ----------------------------------------------------------------------------- */

void Swig_typemap_register(const_String_or_char_ptr tmap_method, ParmList *parms, const_String_or_char_ptr code, ParmList *locals, ParmList *kwargs) {
  String *parms_str = ParmList_str_multibrackets(parms);
  String *source_directive = NewStringf("typemap(%s) %s", tmap_method, parms_str);

  typemap_register(tmap_method, parms, code, locals, kwargs, source_directive);

  Delete(source_directive);
  Delete(parms_str);
}

/* -----------------------------------------------------------------------------
 * typemap_get()
 *
 * Retrieve typemap information.
 * ----------------------------------------------------------------------------- */

static Hash *typemap_get(SwigType *type, const_String_or_char_ptr name) {
  Hash *tm, *tm1;
  tm = get_typemap(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_ptr tmap_method, ParmList *srcparms, ParmList *parms) {
  Hash *tm = 0;
  String *tm_method;
  Parm *p;
  String *pname;
  SwigType *ptype;
  String *tm_methods, *multi_tmap_method;
  if (ParmList_len(parms) != ParmList_len(srcparms))
    return -1;

  tm_method = typemap_method_name(tmap_method);
  p = srcparms;
  tm_methods = NewString(tm_method);
  while (p) {
    ptype = Getattr(p, "type");
    pname = Getattr(p, "name");

    /* Lookup the type */
    tm = typemap_get(ptype, pname);
    if (!tm)
      break;

    tm = Getattr(tm, tm_methods);
    if (!tm)
      break;

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

  if (!p && tm) {
    /* Got some kind of match */
    String *parms_str = ParmList_str_multibrackets(parms);
    String *srcparms_str = ParmList_str_multibrackets(srcparms);
    String *source_directive = NewStringf("typemap(%s) %s = %s", tmap_method, parms_str, srcparms_str);

    typemap_register(tmap_method, parms, Getattr(tm, "code"), Getattr(tm, "locals"), Getattr(tm, "kwargs"), source_directive);

    Delete(source_directive);
    Delete(srcparms_str);
    Delete(parms_str);
    return 0;
  }

  /* Not found */
  return -1;
}

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

void Swig_typemap_clear(const_String_or_char_ptr tmap_method, ParmList *parms) {
  SwigType *type;
  String *name;
  Parm *p;
  String *multi_tmap_method;
  Hash *tm = 0;

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

/* -----------------------------------------------------------------------------
 * 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;
  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 = NewStringEmpty();
  dsig = NewStringEmpty();
  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 */
  type = Getattr(lastdp, "type");
  tm = get_typemap(type);
  if (!tm) {
    set_typemap(type, &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");

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

  /* If there is no exact match on the type, look for a typemap by resolving the type
     until/if a suitable match on the type is found, such as

     typedef unsigned long size_t;
     ...
     %apply(size_t) {my_size};  ==>  %apply(unsigned long) {my_size};
   */
  SwigType *ntype = Copy(type);
  while (!sm && ntype) {
    SwigType *nt = ntype;
    ntype = SwigType_typedef_resolve(ntype);
    if (ntype) {
      sm = typemap_get(ntype, name);
    }
    Delete(nt);
  }
  Delete(ntype);

  if (sm) {
    /* Got a typemap.  Need to only merge attributes for methods that match our signature */
    Iterator ki;
    Hash *deferred_add;
    match = 1;

    /* Since typemap_register can modify the `sm` hash, we *cannot* call typemap_register while iterating over sm.
     * Create a temporary hash of typemaps to add immediately after. */
    deferred_add = NewHash();
    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;
          Hash *sm1 = ki.item;

          code = Getattr(sm1, "code");
          if (code) {
            Replace(nkey, dsig, "", DOH_REPLACE_ANY);
            Replace(nkey, "tmap:", "", DOH_REPLACE_ANY);
            Setattr(deferred_add, nkey, sm1);
          }
        }
        Delete(nkey);
      }
    }

    /* After assembling the key/item pairs, add the resulting typemaps */
    for (ki = First(deferred_add); ki.key; ki = Next(ki)) {
      Hash *sm1 = ki.item;
      String *src_str = ParmList_str_multibrackets(src);
      String *dest_str = ParmList_str_multibrackets(dest);
      String *source_directive = NewStringf("apply %s { %s }", src_str, dest_str);

      typemap_register(ki.key, dest, Getattr(sm1, "code"), Getattr(sm1, "locals"), Getattr(sm1, "kwargs"), source_directive);

      Delete(source_directive);
      Delete(dest_str);
      Delete(src_str);
    }
    Delete(deferred_add);
  }
  Delete(ssig);
  Delete(dsig);
  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 = NewStringEmpty();
  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 = get_typemap(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;
    char *ctsig = Char(tsig);
    for (ki = First(tm); ki.key; ki = Next(ki)) {
      char *ckey = Char(ki.key);
      if (strncmp(ckey, "tmap:", 5) == 0) {
        int na = count_args(ki.key);
        if ((na == narg) && strstr(ckey, ctsig)) {
          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;
}

static void debug_search_result_display(Node *tm) {
  if (tm)
    Printf(stdout, "  Using: %%%s\n", Getattr(tm, "source"));
  else
    Printf(stdout, "  None found\n");
}

/* -----------------------------------------------------------------------------
 * typemap_search_helper()
 *
 * Helper function for typemap_search to see if there is a type match in the typemap
 * tm. A match is sought in this order:
 * %typemap(tm_method) ctype cqualifiedname
 * %typemap(tm_method) ctype cname
 * %typemap(tm_method) ctype
 * ----------------------------------------------------------------------------- */

static Hash *typemap_search_helper(int debug_display, Hash *tm, const String *tm_method, SwigType *ctype, const String *cqualifiedname, const String *cname,
                                   Hash **backup) {
  Hash *result = 0;
  Hash *tm1;
  if (debug_display && cqualifiedname)
    Printf(stdout, "  Looking for: %s\n", SwigType_str(ctype, cqualifiedname));
  if (tm && cqualifiedname) {
    tm1 = Getattr(tm, cqualifiedname);
    if (tm1) {
      result = Getattr(tm1, tm_method); /* See if there is a type - qualified name match */
      if (result && Getattr(result, "code"))
        goto ret_result;
      if (result)
        *backup = result;
    }
  }
  if (debug_display && cname)
    Printf(stdout, "  Looking for: %s\n", SwigType_str(ctype, cname));
  if (tm && cname) {
    tm1 = Getattr(tm, cname);
    if (tm1) {
      result = Getattr(tm1, tm_method); /* See if there is a type - name match */
      if (result && Getattr(result, "code"))
        goto ret_result;
      if (result)
        *backup = result;
    }
  }
  if (debug_display)
    Printf(stdout, "  Looking for: %s\n", SwigType_str(ctype, 0));
  if (tm) {
    result = Getattr(tm, tm_method); /* See if there is simply a type without name match */
    if (result && Getattr(result, "code"))
      goto ret_result;
    if (result)
      *backup = result;
  }
ret_result:
  return result;
}

/* -----------------------------------------------------------------------------
 * typemap_search()
 *
 * Search for a typemap match. This is where the typemap pattern matching rules
 * are implemented... tries to find the most specific typemap that includes a
 * 'code' attribute.
 * ----------------------------------------------------------------------------- */

static Hash *typemap_search(const_String_or_char_ptr tmap_method, SwigType *type, const_String_or_char_ptr name, const_String_or_char_ptr qualifiedname,
                            SwigType **matchtype, Node *node) {
  Hash *result = 0;
  Hash *tm;
  Hash *backup = 0;
  SwigType *primitive = 0;
  SwigType *ctype = 0;
  SwigType *ctype_unstripped = 0;
  int isarray;
  const String *cname = 0;
  const String *cqualifiedname = 0;
  String *tm_method = typemap_method_name(tmap_method);
  int debug_display = (in_typemap_search_multi == 0) && typemap_search_debug;

  if ((name) && Len(name))
    cname = name;
  if ((qualifiedname) && Len(qualifiedname))
    cqualifiedname = qualifiedname;

  if (debug_display) {
    String *typestr = SwigType_str(type, cqualifiedname ? cqualifiedname : cname);
    Swig_diagnostic(Getfile(node), Getline(node), "Searching for a suitable '%s' typemap for: %s\n", tmap_method, typestr);
    Delete(typestr);
  }
  ctype = Copy(type);
  ctype_unstripped = Copy(ctype);
  while (ctype) {
    /* Try to get an exact type-match */
    tm = get_typemap(ctype);
    result = typemap_search_helper(debug_display, tm, tm_method, ctype, cqualifiedname, cname, &backup);
    if (result && Getattr(result, "code"))
      goto ret_result;

    {
      /* Look for the type reduced to just the template prefix - for templated types without the template parameter list being specified */
      SwigType *template_prefix = SwigType_istemplate_only_templateprefix(ctype);
      if (template_prefix) {
        tm = get_typemap(template_prefix);
        result = typemap_search_helper(debug_display, tm, tm_method, template_prefix, cqualifiedname, cname, &backup);
        Delete(template_prefix);
        if (result && Getattr(result, "code"))
          goto ret_result;
      }
    }

    /* look for [ANY] arrays */
    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 */
      SwigType *noarrays = strip_arrays(ctype);
      tm = get_typemap(noarrays);
      result = typemap_search_helper(debug_display, tm, tm_method, noarrays, cqualifiedname, cname, &backup);
      Delete(noarrays);
      if (result && Getattr(result, "code"))
        goto ret_result;
    }

    /* No match so far - try with a qualifier stripped (strip one qualifier at a time until none remain)
     * The order of stripping in SwigType_strip_single_qualifier is used to provide some sort of consistency
     * with the default (SWIGTYPE) typemap matching rules for the first qualifier to be stripped. */
    {
      SwigType *oldctype = ctype;
      ctype = SwigType_strip_single_qualifier(oldctype);
      if (!Equal(ctype, oldctype)) {
        Delete(oldctype);
        continue;
      }
      Delete(oldctype);
    }

    /* Once all qualifiers are stripped try resolve a typedef */
    {
      SwigType *oldctype = ctype;
      ctype = SwigType_typedef_resolve(ctype_unstripped);
      Delete(oldctype);
      Delete(ctype_unstripped);
      ctype_unstripped = Copy(ctype);
    }
  }

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

  primitive = SwigType_default_create(type);
  while (primitive) {
    tm = get_typemap(primitive);
    result = typemap_search_helper(debug_display, tm, tm_method, primitive, cqualifiedname, cname, &backup);
    if (result && Getattr(result, "code"))
      goto ret_result;

    {
      SwigType *nprim = SwigType_default_deduce(primitive);
      Delete(primitive);
      primitive = nprim;
    }
  }
  if (ctype != type) {
    Delete(ctype);
    ctype = 0;
  }
  result = backup;

ret_result:
  Delete(primitive);
  if (matchtype)
    *matchtype = Copy(ctype);
  Delete(ctype);
  Delete(ctype_unstripped);
  return result;
}

/* -----------------------------------------------------------------------------
 * typemap_search_multi()
 *
 * Search for a multi-argument typemap.
 * ----------------------------------------------------------------------------- */

static Hash *typemap_search_multi(const_String_or_char_ptr tmap_method, ParmList *parms, int *nmatch) {
  SwigType *type;
  SwigType *mtype = 0;
  String *name;
  String *multi_tmap_method;
  Hash *tm;
  Hash *tm1 = 0;

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

  /* Try to find a match on the first type */
  tm = typemap_search(tmap_method, type, name, 0, &mtype, parms);
  if (tm) {
    if (mtype && SwigType_isarray(mtype)) {
      Setattr(parms, "tmap:match", mtype);
    }
    Delete(mtype);
    multi_tmap_method = NewStringf("%s-%s+%s:", tmap_method, type, name);
    in_typemap_search_multi++;
    tm1 = typemap_search_multi(multi_tmap_method, nextSibling(parms), nmatch);
    in_typemap_search_multi--;
    if (tm1)
      tm = tm1;
    if (Getattr(tm, "code")) {
      *(nmatch) = *nmatch + 1;
      if (typemap_search_debug && tm1 && (in_typemap_search_multi == 0)) {
        Printf(stdout, "  Multi-argument typemap found...\n");
      }
    } else {
      tm = 0;
    }
    Delete(multi_tmap_method);
  }

  if (typemap_search_debug && (in_typemap_search_multi == 0))
    debug_search_result_display(tm);
  if (typemaps_used_debug && (in_typemap_search_multi == 0) && tm) {
    String *typestr = SwigType_str(type, name);
    Swig_diagnostic(Getfile(parms), Getline(parms), "Typemap for %s (%s) : %%%s\n", typestr, tmap_method, Getattr(tm, "source"));
    assert(Getfile(parms) && Len(Getfile(parms)) > 0); /* Missing file and line numbering information */
    Delete(typestr);
  }

  return tm;
}

static int replace_with_override(String *src, const DOHString_or_char *token, const DOHString_or_char *replace, int flags, Hash *override_vars) {
  String *override_replace = override_vars ? Getattr(override_vars, token) : NULL;
  if (override_replace)
    return Replace(src, token, override_replace, flags);
  return Replace(src, token, replace, flags);
}

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

static void replace_local_names(ParmList *p, const String *name, const String *replace, Hash *override_vars) {
  while (p) {
    int replaced = 0;
    SwigType *n = Getattr(p, "name");
    SwigType *origname = Copy(n);
    replaced = replace_with_override(n, name, replace, DOH_REPLACE_NUMBER_END, override_vars);
    if (replaced > 0 && !Getattr(p, "origname"))
      Setattr(p, "origname", origname);
    Delete(origname);
    p = nextSibling(p);
  }
}

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

/* -----------------------------------------------------------------------------
 * typemap_replace_vars()
 *
 * Replaces typemap variables in a string.  index is the $n variable.
 * type and pname are the type and parameter name.
 * override_vars are only used to replace variable names (NOT types), so is only
 * used for $n, but not the various ways of representing types: $n_ltype, $n_type etc
 * ----------------------------------------------------------------------------- */

static int typemap_replace_vars(String *s, ParmList *locals, SwigType *type, SwigType *rtype, String *pname, String *lname, int index, Hash *override_vars) {
  char var[512];
  char *varname;
  size_t varname_size;
  SwigType *ftype;
  int bare_substitution_count = 0;

  Replaceall(s, "$typemap", "$TYPEMAP"); /* workaround for $type substitution below */

  ftype = SwigType_typedef_resolve_all(type);

  if (!pname)
    pname = lname;
  {
    int replace = 0;
    Parm *p = locals;
    while (p) {
      String *pname = Getattr(p, "name");
      if (Strchr(Getattr(p, "type"), '$') || (pname && Strchr(pname, '$')))
        replace = 1;
      p = nextSibling(p);
    }
    if (!replace)
      locals = 0;
  }

  snprintf(var, sizeof(var), "$%d_", index);
  varname = &var[strlen(var)];
  varname_size = sizeof(var) - (size_t)(varname - var);

  /* If the original datatype was an array. We're going to go through and substitute
     its array dimensions */

  if (SwigType_isarray(type) || SwigType_isarray(ftype)) {
    String *size;
    int ndim;
    int i;
    if (SwigType_array_ndim(type) != SwigType_array_ndim(ftype))
      type = ftype;
    ndim = SwigType_array_ndim(type);
    size = NewStringEmpty();
    for (i = 0; i < ndim; i++) {
      String *dim = SwigType_array_getdim(type, i);
      if (index == 1) {
        char t[32];
        snprintf(t, sizeof(t), "$dim%d", i);
        Replace(s, t, dim, DOH_REPLACE_ANY);
        replace_local_types(locals, t, dim);
      }
      snprintf(varname, varname_size, "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);
    }
    snprintf(varname, varname_size, "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, *lex_type;
    SwigType *ltype, *star_ltype, *amp_ltype;
    String *mangle, *star_mangle, *amp_mangle, *base_mangle, *base_name, *base_type_str;
    String *descriptor, *star_descriptor, *amp_descriptor;
    String *ts;
    char *sc;

    sc = Char(s);

    if (strstr(sc, "type") || check_locals_type(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_type(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)) || (SwigType_isrvalue_reference(ftype))) {
      if (!(SwigType_isarray(type) || SwigType_ispointer(type) || SwigType_isreference(type) || SwigType_isrvalue_reference(type))) {
        star_type = Copy(ftype);
      } else {
        star_type = Copy(type);
      }
      if (!(SwigType_isreference(star_type) || SwigType_isrvalue_reference(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);
        }
        snprintf(varname, varname_size, "$*%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);
      }
      snprintf(varname, varname_size, "$*%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);

      snprintf(varname, varname_size, "$*%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);
      snprintf(varname, varname_size, "$*%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);
    }
    snprintf(varname, varname_size, "$&%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);
    }
    snprintf(varname, varname_size, "$&%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);
    snprintf(varname, varname_size, "$&%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);
    snprintf(varname, varname_size, "$&%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)) {
      base_type = Copy(type);
      Delete(SwigType_pop_arrays(base_type));
    } else {
      base_type = SwigType_base(type);
    }

    base_type_str = SwigType_str(base_type, 0);
    base_name = SwigType_namestr(base_type_str);
    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_str, 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_name);
    Delete(base_type_str);
    Delete(base_type);

    lex_type = SwigType_base(rtype);
    if (index == 1)
      Replace(s, "$lextype", lex_type, DOH_REPLACE_ANY);
    strcpy(varname, "lextype");
    Replace(s, var, lex_type, DOH_REPLACE_ANY);
    Delete(lex_type);
  }

  /* Replace variable usage $n. with (&$n)-> */
  {
    char temp[64];
    snprintf(var, sizeof(var), "$%d.", index);
    snprintf(temp, sizeof(temp), "(&$%d)->", index);
    Replace(s, var, temp, DOH_REPLACE_ANY);
  }

  /* Replace the bare $n variable */
  snprintf(var, sizeof(var), "$%d", index);
  bare_substitution_count = replace_with_override(s, var, lname, DOH_REPLACE_NUMBER_END, override_vars);
  replace_local_names(locals, var, lname, override_vars);

  Delete(ftype);
  return bare_substitution_count;
}

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

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

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

        str = NewStringEmpty();

        if (strncmp(Char(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 && !Getattr(p, "origname")) {
          Printf(str, "%s%d", pn, argnum);
        } else {
          Append(str, pn);
        }
        if (isglobal && Wrapper_check_local(f, str)) {
          p = nextSibling(p);
          Delete(str);
          if (at)
            Delete(at);
          continue;
        }
        if (value) {
          String *pstr = SwigType_str(pt, str);
          new_name = Wrapper_new_localv(f, str, pstr, "=", value, NIL);
          Delete(pstr);
        } else {
          String *pstr = SwigType_str(pt, str);
          new_name = Wrapper_new_localv(f, str, pstr, NIL);
          Delete(pstr);
        }
        if (!isglobal) {
          /* Substitute  */
          Replace(s, pn, new_name, DOH_REPLACE_ID | DOH_REPLACE_NOQUOTE);
        }
        Delete(str);
      }
    }
    p = nextSibling(p);
    if (at)
      Delete(at);
  }
}

/* -----------------------------------------------------------------------------
 * typemap_warn()
 *
 * If any warning message is attached to this parameter's "tmap:<method>:warning"
 * attribute, return the warning message (special variables will need expanding
 * before displaying the warning).
 * ----------------------------------------------------------------------------- */

static String *typemap_warn(const_String_or_char_ptr tmap_method, Parm *p) {
  String *temp = NewStringf("%s:warning", tmap_method);
  String *w = Getattr(p, typemap_method_name(temp));
  Delete(temp);
  return w ? Copy(w) : 0;
}

/* -----------------------------------------------------------------------------
 * typemap_merge_fragment_kwargs()
 *
 * If multiple 'fragment' attributes are provided to a typemap, combine them by
 * concatenating with commas.
 * ----------------------------------------------------------------------------- */

static void typemap_merge_fragment_kwargs(Parm *kw) {
  Parm *reattach_kw = NULL;
  Parm *prev_kw = NULL;
  Parm *next_kw = NULL;
  String *fragment = NULL;
  while (kw) {
    next_kw = nextSibling(kw);
    if (Strcmp(Getattr(kw, "name"), "fragment") == 0) {
      String *thisfragment = Getattr(kw, "value");
      String *kwtype = Getattr(kw, "type");
      if (!fragment) {
        /* First fragment found; it should remain in the list */
        fragment = thisfragment;
        prev_kw = kw;
      } else {
        /* Concatenate to previously found fragment */
        Printv(fragment, ",", thisfragment, NULL);
        reattach_kw = prev_kw;
      }
      if (kwtype) {
        String *mangle = Swig_name_mangle_type(kwtype);
        Append(fragment, mangle);
        Delete(mangle);
        /* Remove 'type' from kwargs so it's not duplicated later */
        Setattr(kw, "type", NULL);
      }
    } else {
      /* Not a fragment */
      if (reattach_kw) {
        /* Update linked list to remove duplicate fragment */
        DohIncref(kw);
        set_nextSibling(reattach_kw, kw);
        set_previousSibling(kw, reattach_kw);
        Delete(reattach_kw);
        reattach_kw = NULL;
      }
      prev_kw = kw;
    }
    kw = next_kw;
  }
  if (reattach_kw) {
    /* Update linked list to remove duplicate fragment */
    set_nextSibling(reattach_kw, kw);
  }
}

/* -----------------------------------------------------------------------------
 * Swig_typemap_lookup()
 *
 * Attach one or more typemaps to a node and optionally generate the typemap contents
 * into the wrapper.
 *
 * Looks for a typemap matching the given type and name and attaches the typemap code
 * and any typemap attributes to the provided node.
 *
 * The node should contain the "type" and "name" attributes for the typemap match on.
 * input. The typemap code and typemap attribute values are attached onto the node
 * prefixed with "tmap:". For example with tmap_method="in", the typemap code can be retrieved
 * with a call to Getattr(node, "tmap:in") (this is also the string returned) and the
 * "noblock" attribute can be retrieved with a call to Getattr(node, "tmap:in:noblock").
 *
 * tmap_method - typemap method, eg "in", "out", "newfree"
 * node        - the node to attach the typemap and typemap attributes to
 * lname       - name of variable to substitute $1, $2 etc for
 * f           - wrapper code to generate into if non null
 * actioncode  - code to generate into f before the out typemap code, unless
 *              the optimal attribute is set in the out typemap in which case
 *              $1 in the out typemap will be replaced  by the code in actioncode.
 * ----------------------------------------------------------------------------- */

static String *Swig_typemap_lookup_impl(const_String_or_char_ptr tmap_method, Node *node, const_String_or_char_ptr lname, Wrapper *f, String *actioncode) {
  SwigType *type;
  SwigType *mtype = 0;
  String *pname;
  String *qpname = 0;
  String *noscope_pname = 0;
  Hash *tm = 0;
  String *s = 0;
  String *sdef = 0;
  String *warning = 0;
  ParmList *locals;
  ParmList *kw;
  char temp[256];
  String *symname;
  String *cname = 0;
  String *clname = 0;
  char *cmethod = Char(tmap_method);
  int optimal_attribute = 0;
  int optimal_substitution = 0;
  int delete_optimal_attribute = 0;
  int num_substitutions = 0;
  SwigType *matchtype = 0;

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

  /* Special hook (hack!). Check for the 'ref' feature and add code it contains to any 'newfree' typemap code.
   * We could choose to put this hook into a number of different typemaps, not necessarily 'newfree'...
   * Rather confusingly 'newfree' is used to release memory and the 'ref' feature is used to add in memory references - yuck! */
  if (Cmp(tmap_method, "newfree") == 0) {
    String *base = SwigType_base(type);
    Node *typenode = Swig_symbol_clookup(base, 0);
    if (typenode)
      sdef = Swig_ref_call(typenode, lname);
    Delete(base);
  }

  pname = Getattr(node, "name");
  noscope_pname = Copy(pname);

  if (pname && Getattr(node, "sym:symtab")) {
    /* Add on a qualified name search for any symbol in the symbol table, for example:
     * struct Foo {
     *   int *foo(int bar)   ->  Foo::foo
     * };
     * Note that if node is a parameter (Parm *) then there will be no symbol table attached to the Parm *.
     */
    String *qsn;
    if (Swig_scopename_check(pname)) {
      /* sometimes pname is qualified, so we remove all the scope for the lookup */
      Delete(noscope_pname);
      noscope_pname = Swig_scopename_last(pname);
      /*
      Printf(stdout, "Removed scope: %s => %s\n", pname, noscope_pname);
      */
    }
    qsn = Swig_symbol_qualified(node);
    if (qsn && Len(qsn)) {
      qpname = NewStringf("%s::%s", qsn, noscope_pname);
      Delete(qsn);
    }
  }

  tm = typemap_search(tmap_method, type, noscope_pname, qpname, &mtype, node);
  if (typemap_search_debug)
    debug_search_result_display(tm);
  if (typemaps_used_debug && tm) {
    String *typestr = SwigType_str(type, qpname ? qpname : pname);
    Swig_diagnostic(Getfile(node), Getline(node), "Typemap for %s (%s) : %%%s\n", typestr, tmap_method, Getattr(tm, "source"));
    assert(Getfile(node) && Len(Getfile(node)) > 0); /* Missing file and line numbering information */
    Delete(typestr);
  }

  Delete(qpname);
  qpname = 0;
  Delete(noscope_pname);
  noscope_pname = 0;

  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 */

  /* Look in the "out" typemap for the "optimal" attribute */
  if (Cmp(cmethod, "out") == 0) {
    kw = Getattr(tm, "kwargs");
    while (kw) {
      if (Cmp(Getattr(kw, "name"), "optimal") == 0) {
        optimal_attribute = GetFlag(kw, "value");
        break;
      }
      kw = nextSibling(kw);
    }
  }

  if (optimal_attribute) {
    /* Note: "out" typemap is the only typemap that will have the "optimal" attribute set.
     * If f and actioncode are NULL, then the caller is just looking to attach the "out" attributes
     * ie, not use the typemap code, otherwise both f and actioncode must be non null. */
    if (actioncode) {
      const String *result_equals = NewStringf("%s = ", Swig_cresult_name());
      /* check that the code in the typemap can be used in this optimal way.
       * The code should be in the form "result = ...;\n". We need to extract
       * the "..." part. This may not be possible for various reasons, eg
       * code added by %exception. This optimal code generation is bit of a
       * hack and circumvents the normal requirement for a temporary variable
       * to hold the result returned from a wrapped function call.
       */
      if (Strncmp(actioncode, result_equals, Len(result_equals)) == 0 && Strchr(actioncode, ';') == Char(actioncode) + Len(actioncode) - 2 &&
          Char(actioncode)[Len(actioncode) - 1] == '\n') {
        clname = NewStringWithSize(Char(actioncode) + Len(result_equals), Len(actioncode) - Len(result_equals) - 2);
        lname = clname;
        actioncode = 0;
        optimal_substitution = 1;
      } else {
        Swig_warning(
          WARN_TYPEMAP_OUT_OPTIMAL_IGNORED, Getfile(node), Getline(node), "Method %s usage of the optimal attribute ignored\n", Swig_name_decl(node));
        Swig_warning(WARN_TYPEMAP_OUT_OPTIMAL_IGNORED,
                     Getfile(s),
                     Getline(s),
                     "in the out typemap as the following cannot be used to generate optimal code: %s\n",
                     actioncode);
        delete_optimal_attribute = 1;
      }
    } else {
      assert(!f);
    }
  }

  if (actioncode) {
    assert(f);
    Append(f->code, actioncode);
  }

  /* emit local variables declared in typemap, eg emit declarations for aa and bb in:
   * %typemap(in) foo (int aa, int bb) "..." */
  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;
  }

  matchtype = mtype && SwigType_isarray(mtype) ? mtype : type;
  num_substitutions = typemap_replace_vars(s, locals, matchtype, type, pname, (char *)lname, 1, NULL);
  if (optimal_substitution && num_substitutions > 1) {
    Swig_warning(WARN_TYPEMAP_OUT_OPTIMAL_MULTIPLE, Getfile(node), Getline(node), "Multiple calls to %s might be generated due to\n", Swig_name_decl(node));
    Swig_warning(WARN_TYPEMAP_OUT_OPTIMAL_MULTIPLE, Getfile(s), Getline(s), "optimal attribute usage in the out typemap.\n");
  }

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

  {
    ParmList *parm_sublist = NewParmWithoutFileLineInfo(type, pname);
    Setattr(parm_sublist, "lname", lname);
    replace_embedded_typemap(s, parm_sublist, f, tm);
    Delete(parm_sublist);
  }

  /* Attach kwargs - ie the typemap attributes */
  kw = Getattr(tm, "kwargs");
  typemap_merge_fragment_kwargs(kw);
  while (kw) {
    String *value = Copy(Getattr(kw, "value"));
    String *kwtype = Getattr(kw, "type");
    char *ckwname = Char(Getattr(kw, "name"));
    {
      /* Expand special variables in typemap attributes. */
      SwigType *ptype = Getattr(node, "type");
      String *pname = Getattr(node, "name");
      SwigType *mtype = Getattr(node, "tmap:match");
      SwigType *matchtype = mtype ? mtype : ptype;
      ParmList *parm_sublist;
      typemap_replace_vars(value, NULL, matchtype, ptype, pname, (char *)lname, 1, NULL);

      /* Expand special variable macros (embedded typemaps) in typemap attributes. */
      parm_sublist = NewParmWithoutFileLineInfo(ptype, pname);
      Setattr(parm_sublist, "lname", lname);
      replace_embedded_typemap(value, parm_sublist, NULL, tm);
      Delete(parm_sublist);
    }
    if (kwtype) {
      String *mangle = Swig_name_mangle_type(kwtype);
      Append(value, mangle);
      Delete(mangle);
    }
    snprintf(temp, sizeof(temp), "%s:%s", cmethod, ckwname);
    Setattr(node, typemap_method_name(temp), value);
    Delete(value);
    kw = nextSibling(kw);
  }

  if (delete_optimal_attribute)
    Delattr(node, "tmap:out:optimal");

  Replace(s, "$name", pname, DOH_REPLACE_ANY);

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

  Setattr(node, typemap_method_name(tmap_method), s);
  if (locals) {
    snprintf(temp, sizeof(temp), "%s:locals", cmethod);
    Setattr(node, typemap_method_name(temp), locals);
    Delete(locals);
  }

  if (Checkattr(tm, "type", "SWIGTYPE")) {
    snprintf(temp, sizeof(temp), "%s:SWIGTYPE", cmethod);
    Setattr(node, typemap_method_name(temp), "1");
  }

  /* Print warnings, if any */
  warning = typemap_warn(cmethod, node);
  if (warning) {
    typemap_replace_vars(warning, 0, matchtype, type, pname, (char *)lname, 1, NULL);
    Replace(warning, "$name", pname, DOH_REPLACE_ANY);
    if (symname)
      Replace(warning, "$symname", symname, DOH_REPLACE_ANY);
    Swig_warning(0, Getfile(node), Getline(node), "%s\n", warning);
    Delete(warning);
  }

  /* Look for code fragments */
  {
    String *fragment;
    snprintf(temp, sizeof(temp), "%s:fragment", cmethod);
    fragment = Getattr(node, typemap_method_name(temp));
    if (fragment) {
      String *fname = Copy(fragment);
      Setfile(fname, Getfile(node));
      Setline(fname, Getline(node));
      Swig_fragment_emit(fname);
      Delete(fname);
    }
  }

  Delete(cname);
  Delete(clname);
  Delete(mtype);
  if (sdef) { /* put 'ref' and 'newfree' codes together */
    String *p = NewStringf("%s\n%s", sdef, s);
    Delete(s);
    Delete(sdef);
    s = p;
  }
  Delete(actioncode);
  return s;
}

String *Swig_typemap_lookup_out(const_String_or_char_ptr tmap_method, Node *node, const_String_or_char_ptr lname, Wrapper *f, String *actioncode) {
  assert(actioncode);
  assert(Cmp(tmap_method, "out") == 0);
  return Swig_typemap_lookup_impl(tmap_method, node, lname, f, actioncode);
}

String *Swig_typemap_lookup(const_String_or_char_ptr tmap_method, Node *node, const_String_or_char_ptr lname, Wrapper *f) {
  return Swig_typemap_lookup_impl(tmap_method, node, lname, f, 0);
}

/* -----------------------------------------------------------------------------
 * 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 firstp (first parameter in parameter list):
 * %typemap(in, foo="xyz") ...
 * A new attribute called "tmap:in:foo" with value "xyz" is attached to firstp.
 * Also expands special variables and special variable macros in the typemap attributes.
 * ----------------------------------------------------------------------------- */

static void typemap_attach_kwargs(Hash *tm, const_String_or_char_ptr tmap_method, Parm *firstp, int nmatch) {
  String *temp = NewStringEmpty();
  Parm *kw = Getattr(tm, "kwargs");
  typemap_merge_fragment_kwargs(kw);
  while (kw) {
    String *value = Copy(Getattr(kw, "value"));
    String *type = Getattr(kw, "type");
    int i;
    Parm *p = firstp;
    /* Expand special variables */
    for (i = 0; i < nmatch; i++) {
      SwigType *type = Getattr(p, "type");
      String *pname = Getattr(p, "name");
      String *lname = Getattr(p, "lname");
      SwigType *mtype = Getattr(p, "tmap:match");
      SwigType *matchtype = mtype ? mtype : type;
      typemap_replace_vars(value, NULL, matchtype, type, pname, lname, i + 1, NULL);
      p = nextSibling(p);
    }

    /* Expand special variable macros (embedded typemaps).
     * Special variable are expanded first above as they might be used in the special variable macros.
     * For example: $typemap(imtype, $2_type). */
    p = firstp;
    for (i = 0; i < nmatch; i++) {
      SwigType *type = Getattr(p, "type");
      String *pname = Getattr(p, "name");
      String *lname = Getattr(p, "lname");
      ParmList *parm_sublist = NewParmWithoutFileLineInfo(type, pname);
      Setattr(parm_sublist, "lname", lname);
      replace_embedded_typemap(value, parm_sublist, NULL, tm);
      p = nextSibling(p);
    }
    if (type) {
      Hash *v = NewHash();
      Setattr(v, "type", type);
      Setattr(v, "value", value);
      Delete(value);
      value = v;
    }
    Clear(temp);
    Printf(temp, "%s:%s", tmap_method, Getattr(kw, "name"));
    Setattr(firstp, typemap_method_name(temp), value);
    Delete(value);
    kw = nextSibling(kw);
  }
  Clear(temp);
  Printf(temp, "%s:match_type", tmap_method);
  Setattr(firstp, typemap_method_name(temp), Getattr(tm, "type"));
  Delete(temp);
}

static void typemap_emit_code_fragments(const_String_or_char_ptr tmap_method, Parm *p) {
  String *temp = NewStringf("%s:fragment", tmap_method);
  String *f = Getattr(p, typemap_method_name(temp));
  if (f) {
    String *fname = Copy(f);
    Setfile(fname, Getfile(p));
    Setline(fname, Getline(p));
    Swig_fragment_emit(fname);
    Delete(fname);
  }
  Delete(temp);
}

static String *typemap_get_option(Hash *tm, const_String_or_char_ptr name) {
  Parm *kw = Getattr(tm, "kwargs");
  while (kw) {
    String *kname = Getattr(kw, "name");
    if (Equal(kname, name)) {
      return Getattr(kw, "value");
    }
    kw = nextSibling(kw);
  }
  return 0;
}

/* -----------------------------------------------------------------------------
 * Swig_typemap_attach_parms()
 *
 * Given a parameter list, this function attaches all of the typemaps and typemap
 * attributes to the parameter for each type in the parameter list.
 *
 * This function basically provides the typemap code and typemap attribute values as
 * attributes on each parameter prefixed with "tmap:". For example with tmap_method="in", the typemap
 * code can be retrieved for the first parameter with a call to Getattr(parm, "tmap:in")
 * and the "numinputs" attribute can be retrieved with a call to Getattr(parm, "tmap:in:numinputs").
 *
 * tmap_method   - typemap method, eg "in", "out", "newfree"
 * parms         - parameter list to attach each typemap and all typemap attributes
 * f             - wrapper code to generate into if non null
 * override_vars - Hash of variables from $typemap(...) that will override special variable replacements
 * ----------------------------------------------------------------------------- */

static void typemap_attach_parms(const_String_or_char_ptr tmap_method, ParmList *parms, Wrapper *f, Hash *override_vars) {
  Parm *p, *firstp;
  Hash *tm;
  int nmatch = 0;
  int i;
  String *s;
  String *warning = 0;
  ParmList *locals;
  int argnum = 0;
  char temp[256];
  char *cmethod = Char(tmap_method);
  String *kwmatch = 0;
  p = parms;

#ifdef SWIG_DEBUG
  Printf(stdout, "Swig_typemap_attach_parms:  %s\n", tmap_method);
#endif

  while (p) {
    argnum++;
    nmatch = 0;
#ifdef SWIG_DEBUG
    Printf(stdout, "parms:  %s %s %s\n", tmap_method, Getattr(p, "name"), Getattr(p, "type"));
#endif
    tm = typemap_search_multi(tmap_method, p, &nmatch);
#ifdef SWIG_DEBUG
    if (tm)
      Printf(stdout, "found:  %s\n", tm);
#endif
    if (!tm) {
      p = nextSibling(p);
      continue;
    }
    /*
       Check if the typemap requires to match the type of another
       typemap, for example:

       %typemap(in) SWIGTYPE * (int var) {...}
       %typemap(freearg,match="in") SWIGTYPE * {if (var$argnum) ...}

       here, the freearg typemap requires the "in" typemap to match,
       or the 'var$argnum' variable will not exist.
     */
    kwmatch = typemap_get_option(tm, "match");
    if (kwmatch) {
      String *tmname = NewStringf("tmap:%s", kwmatch);
      String *tmin = Getattr(p, tmname);
      Delete(tmname);
#ifdef SWIG_DEBUG
      if (tm)
        Printf(stdout, "matching:  %s\n", kwmatch);
#endif
      if (tmin) {
        String *tmninp = NewStringf("tmap:%s:numinputs", kwmatch);
        String *ninp = Getattr(p, tmninp);
        Delete(tmninp);
        if (ninp && Equal(ninp, "0")) {
          p = nextSibling(p);
          continue;
        } else {
          SwigType *typetm = Getattr(tm, "type");
          String *temp = NewStringf("tmap:%s:match_type", kwmatch);
          SwigType *typein = Getattr(p, temp);
          Delete(temp);
          if (!Equal(typein, typetm)) {
            p = nextSibling(p);
            continue;
          } else {
            int nnmatch;
            Hash *tmapin = typemap_search_multi(kwmatch, p, &nnmatch);
            String *tmname = Getattr(tm, "pname");
            String *tnname = Getattr(tmapin, "pname");
            if (!(tmname && tnname && Equal(tmname, tnname)) && !(!tmname && !tnname)) {
              p = nextSibling(p);
              continue;
            }
          }
        }
      } else {
        p = nextSibling(p);
        continue;
      }
    }

    s = Getattr(tm, "code");
    if (!s) {
      p = nextSibling(p);
      continue;
    }
#ifdef SWIG_DEBUG
    if (s)
      Printf(stdout, "code:  %s\n", s);
#endif

    /* 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;
#ifdef SWIG_DEBUG
    Printf(stdout, "nmatch:  %d\n", nmatch);
#endif
    for (i = 0; i < nmatch; i++) {
      SwigType *type = Getattr(p, "type");
      String *pname = Getattr(p, "name");
      String *lname = Getattr(p, "lname");
      SwigType *mtype = Getattr(p, "tmap:match");
      SwigType *matchtype = mtype ? mtype : type;

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

      if (Checkattr(tm, "type", "SWIGTYPE")) {
        snprintf(temp, sizeof(temp), "%s:SWIGTYPE", cmethod);
        Setattr(p, typemap_method_name(temp), "1");
      }
      p = nextSibling(p);
    }

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

    replace_embedded_typemap(s, firstp, f, tm);

    /* Attach attributes to object */
#ifdef SWIG_DEBUG
    Printf(stdout, "attach: %s %s %s\n", Getattr(firstp, "name"), typemap_method_name(tmap_method), s);
#endif
    Setattr(firstp, typemap_method_name(tmap_method), s); /* Code object */

    if (locals) {
      snprintf(temp, sizeof(temp), "%s:locals", cmethod);
      Setattr(firstp, typemap_method_name(temp), locals);
      Delete(locals);
    }

    /* Attach a link to the next parameter.  Needed for multimaps */
    snprintf(temp, sizeof(temp), "%s:next", cmethod);
    Setattr(firstp, typemap_method_name(temp), p);

    /* Attach kwargs */
    typemap_attach_kwargs(tm, tmap_method, firstp, nmatch);

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

    /* Print warnings, if any */
    warning = typemap_warn(tmap_method, firstp);
    if (warning) {
      SwigType *type = Getattr(firstp, "type");
      String *pname = Getattr(firstp, "name");
      String *lname = Getattr(firstp, "lname");
      SwigType *mtype = Getattr(firstp, "tmap:match");
      SwigType *matchtype = mtype ? mtype : type;
      typemap_replace_vars(warning, 0, matchtype, type, pname, lname, 1, override_vars);
      Replace(warning, "$argnum", temp, DOH_REPLACE_ANY);
      Swig_warning(0, Getfile(firstp), Getline(firstp), "%s\n", warning);
      Delete(warning);
    }

    /* Look for code fragments */
    typemap_emit_code_fragments(tmap_method, firstp);

    /* increase argnum to consider numinputs */
    argnum += nmatch - 1;
    Delete(s);
#ifdef SWIG_DEBUG
    Printf(stdout, "res: %s %s %s\n", Getattr(firstp, "name"), typemap_method_name(tmap_method), Getattr(firstp, typemap_method_name(tmap_method)));
#endif
  }
#ifdef SWIG_DEBUG
  Printf(stdout, "Swig_typemap_attach_parms: end\n");
#endif
}

void Swig_typemap_attach_parms(const_String_or_char_ptr tmap_method, ParmList *parms, Wrapper *f) {
  typemap_attach_parms(tmap_method, parms, f, NULL);
}

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

  args = NewList();
  c = strchr(Char(s), '(');
  assert(c);
  c++;

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

/* -----------------------------------------------------------------------------
 * Swig_typemap_replace_embedded_typemap()
 *
 * For special variable macro $typemap(...) expansion outside of typemaps.
 * Only limited usage works as most typemap special variables ($1, $input etc)
 * are not expanded correctly outside of typemaps.
 * ----------------------------------------------------------------------------- */

void Swig_typemap_replace_embedded_typemap(String *s, Node *file_line_node) {
  Setfile(s, Getfile(file_line_node));
  Setline(s, Getline(file_line_node));
  Replaceall(s, "$typemap", "$TYPEMAP");
  replace_embedded_typemap(s, 0, 0, file_line_node);
}

/* -----------------------------------------------------------------------------
 * replace_embedded_typemap()
 *
 * This function replaces the special variable macro $typemap(...) with typemap
 * code. The general form of $typemap is as follows:
 *
 *   $typemap(method, typelist, var1=value, var2=value, ...)
 *
 * where varx parameters are optional and undocumented; they were initially an experiment in the first implementation of $typemap.
 * They allow one to override known special variable replacements or even add in new special variables for replacement
 * from the calling typemap to the called typemap.
 * A search is made using the typemap matching rules of form:
 *
 *   %typemap(method) typelist {...}
 *
 * and if found will substitute in the typemap contents, making appropriate variable replacements.
 *
 * For example:
 *   $typemap(in, int)                       # simple usage matching %typemap(in) int { ... }
 *   $typemap(in, int b)                     # simple usage matching %typemap(in) int b { ... } or above %typemap
 *   $typemap(in, (Foo<int, bool> a, int b)) # multi-argument typemap matching %typemap(in) (Foo<int, bool> a, int b) {...}
 * ----------------------------------------------------------------------------- */

static void replace_embedded_typemap(String *s, ParmList *parm_sublist, Wrapper *f, Node *file_line_node) {
  char *start = 0;
  while ((start = strstr(Char(s), "$TYPEMAP("))) { /* note $typemap capitalisation to $TYPEMAP hack */

    /* Gather the parameters */
    char *end = 0, *c;
    int level = 0;
    String *dollar_typemap;
    int syntax_error = 1;
    c = start;
    while (*c) {
      if (*c == '(')
        level++;
      if (*c == ')') {
        level--;
        if (level == 0) {
          end = c + 1;
          break;
        }
      }
      c++;
    }
    if (end) {
      dollar_typemap = NewStringWithSize(start, (int)((end - start)));
      syntax_error = 0;
    } else {
      dollar_typemap = NewStringWithSize(start, (int)((c - start)));
    }

    if (!syntax_error) {
      List *l;
      String *tmap_method;
      Hash *override_vars;
      syntax_error = 1;

      /* Split apart each parameter in $typemap(...) */
      l = split_embedded_typemap(dollar_typemap);

      if (Len(l) >= 2) {
        ParmList *to_match_parms;
        tmap_method = Getitem(l, 0);

        /* the second parameter might contain multiple sub-parameters for multi-argument
         * typemap matching, so split these parameters apart */
        to_match_parms = Swig_cparse_parms(Getitem(l, 1), file_line_node);
        if (to_match_parms) {
          Parm *p = to_match_parms;
          Parm *sub_p = parm_sublist;
          String *empty_string = NewStringEmpty();
          String *lname = empty_string;
          while (p) {
            if (sub_p) {
              lname = Getattr(sub_p, "lname");
              sub_p = nextSibling(sub_p);
            }
            Setattr(p, "lname", lname);
            p = nextSibling(p);
          }
          Delete(empty_string);
        }

        /* process optional extra parameters - the override variable replacements (undocumented) */
        override_vars = NewHash();
        {
          int i, ilen;
          ilen = Len(l);
          for (i = 2; i < ilen; i++) {
            String *parm = Getitem(l, i);
            char *eq = strchr(Char(parm), '=');
            char *c = Char(parm);
            if (eq && (eq - c > 0)) {
              String *name = NewStringWithSize(c, (int)(eq - c));
              String *value = NewString(eq + 1);
              Insert(name, 0, "$");
              Setattr(override_vars, name, value);
            } else {
              to_match_parms = 0; /* error - variable replacement parameters must be of form varname=value */
            }
          }
        }

        /* Perform a typemap search */
        if (to_match_parms) {
          static int already_substituting = 0;
          String *tm;
          String *attr;
          int match = 0;
#ifdef SWIG_DEBUG
          Printf(stdout, "Swig_typemap_attach_parms:  embedded\n");
#endif
          if (already_substituting < 10) {
            char *found_colon;
            already_substituting++;
            if ((in_typemap_search_multi == 0) && typemap_search_debug) {
              String *dtypemap = NewString(dollar_typemap);
              Replaceall(dtypemap, "$TYPEMAP", "$typemap");
              Printf(stdout, "  Containing: %s\n", dtypemap);
              Delete(dtypemap);
            }
            found_colon = Strchr(tmap_method, ':');
            if (found_colon) {
              /* Substitute from a keyword argument to a typemap. Avoid emitting local variables from the attached typemap by passing NULL for the file. */
              String *temp_tmap_method = NewStringWithSize(Char(tmap_method), (int)(found_colon - Char(tmap_method)));
              typemap_attach_parms(temp_tmap_method, to_match_parms, NULL, override_vars);
              Delete(temp_tmap_method);
            } else {
              typemap_attach_parms(tmap_method, to_match_parms, f, override_vars);
            }
            already_substituting--;

            /* Look for the typemap code */
            attr = NewStringf("tmap:%s", tmap_method);
            tm = Getattr(to_match_parms, attr);
            if (tm) {
              Printf(attr, "%s", ":next");
              /* fail if multi-argument lookup requested in $typemap(...) and the lookup failed */
              if (!Getattr(to_match_parms, attr)) {
                /* Replace parameter variables */
                Iterator ki;
                for (ki = First(override_vars); ki.key; ki = Next(ki)) {
                  Replace(tm, ki.key, ki.item, DOH_REPLACE_ANY);
                }
                /* offer the target language module the chance to make special variable substitutions */
                Language_replace_special_variables(tmap_method, tm, to_match_parms);
                /* finish up - do the substitution */
                Replace(s, dollar_typemap, tm, DOH_REPLACE_ANY);
                Delete(tm);
                match = 1;
              }
            }

            if (!match) {
              String *dtypemap = NewString(dollar_typemap);
              Replaceall(dtypemap, "$TYPEMAP", "$typemap");
              Swig_error(Getfile(s), Getline(s), "No typemap found for %s\n", dtypemap);
              Delete(dtypemap);
            }
            Delete(attr);
          } else {
            /* Simple recursive call check to prevent infinite recursion - this strategy only allows a limited
             * number of calls by a embedded typemaps to other embedded typemaps though */
            String *dtypemap = NewString(dollar_typemap);
            Replaceall(dtypemap, "$TYPEMAP", "$typemap");
            Swig_error(Getfile(s), Getline(s), "Likely recursive $typemap calls containing %s. Use -debug-tmsearch to debug.\n", dtypemap);
            Delete(dtypemap);
          }
          syntax_error = 0;
        }
        Delete(override_vars);
      }
      Delete(l);
    }

    if (syntax_error) {
      String *dtypemap = NewString(dollar_typemap);
      Replaceall(dtypemap, "$TYPEMAP", "$typemap");
      Swig_error(Getfile(s), Getline(s), "Syntax error in: %s\n", dtypemap);
      Delete(dtypemap);
    }
    Replace(s, dollar_typemap, "<error in embedded typemap>", DOH_REPLACE_ANY);
    Delete(dollar_typemap);
  }
}

/* -----------------------------------------------------------------------------
 * Swig_typemap_debug()
 *
 * Display all typemaps
 * ----------------------------------------------------------------------------- */

void Swig_typemap_debug(void) {
  int nesting_level = 2;
  Printf(stdout, "---[ typemaps ]--------------------------------------------------------------\n");
  Swig_print(typemaps, nesting_level);
  Printf(stdout, "-----------------------------------------------------------------------------\n");
}

/* -----------------------------------------------------------------------------
 * Swig_typemap_search_debug_set()
 *
 * Turn on typemap searching debug display
 * ----------------------------------------------------------------------------- */

void Swig_typemap_search_debug_set(void) {
  typemap_search_debug = 1;
}

/* -----------------------------------------------------------------------------
 * Swig_typemap_used_debug_set()
 *
 * Turn on typemaps used debug display
 * ----------------------------------------------------------------------------- */

void Swig_typemap_used_debug_set(void) {
  typemaps_used_debug = 1;
}

/* -----------------------------------------------------------------------------
 * Swig_typemap_register_debug_set()
 *
 * Turn on typemaps used debug display
 * ----------------------------------------------------------------------------- */

void Swig_typemap_register_debug_set(void) {
  typemap_register_debug = 1;
}
