typeobj.c   [plain text]


/* -----------------------------------------------------------------------------
 * See the LICENSE file for information on copyright, usage and redistribution
 * of SWIG, and the README file for authors - http://www.swig.org/release.html.
 *
 * typeobj.c
 *
 * This file provides functions for constructing, manipulating, and testing
 * type objects.   Type objects are merely the raw low-level representation
 * of C++ types.   They do not incorporate high-level type system features
 * like typedef, namespaces, etc.
 * ----------------------------------------------------------------------------- */

char cvsroot_typeobj_c[] = "$Id: typeobj.c 11080 2009-01-24 13:15:51Z bhy $";

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

/* -----------------------------------------------------------------------------
 * Synopsis
 *
 * This file provides a collection of low-level functions for constructing and
 * manipulating C++ data types.   In SWIG, C++ datatypes are encoded as simple
 * text strings.  This representation is compact, easy to debug, and easy to read.
 *
 * General idea:
 *
 * Types are represented by a base type (e.g., "int") and a collection of
 * type operators applied to the base (e.g., pointers, arrays, etc...).
 *
 * Encoding:
 *
 * Types are encoded as strings of type constructors such as follows:
 *
 *        String Encoding                 C Example
 *        ---------------                 ---------
 *        p.p.int                         int **
 *        a(300).a(400).int               int [300][400]
 *        p.q(const).char                 char const *
 *
 * All type constructors are denoted by a trailing '.':
 * 
 *  'p.'                = Pointer (*)
 *  'r.'                = Reference (&)
 *  'a(n).'             = Array of size n  [n]
 *  'f(..,..).'         = Function with arguments  (args)
 *  'q(str).'           = Qualifier (such as const or volatile) (const, volatile)
 *  'm(qual).'          = Pointer to member (qual::*)
 *
 * The encoding follows the order that you might describe a type in words.
 * For example "p.a(200).int" is "A pointer to array of int's" and
 * "p.q(const).char" is "a pointer to a const char".
 *
 * This representation of types is fairly convenient because ordinary string
 * operations can be used for type manipulation. For example, a type could be
 * formed by combining two strings such as the following:
 *
 *        "p.p." + "a(400).int" = "p.p.a(400).int"
 *
 * For C++, typenames may be parameterized using <(...)>.  Here are some
 * examples:
 *
 *       String Encoding                  C++ Example
 *       ---------------                  ------------
 *       p.vector<(int)>                  vector<int> *
 *       r.foo<(int,p.double)>            foo<int,double *> &
 *
 * Contents of this file:
 *
 * Most of this functions in this file pertain to the low-level manipulation
 * of type objects.   There are constructor functions like this:
 *
 *       SwigType_add_pointer()
 *       SwigType_add_reference()
 *       SwigType_add_array()
 *
 * These are used to build new types.  There are also functions to undo these
 * operations.  For example:
 *
 *       SwigType_del_pointer()
 *       SwigType_del_reference()
 *       SwigType_del_array()
 *
 * In addition, there are query functions
 *
 *       SwigType_ispointer()
 *       SwigType_isreference()
 *       SwigType_isarray()
 *
 * Finally, there are some data extraction functions that can be used to
 * extract array dimensions, template arguments, and so forth.
 * 
 * It is very important for developers to realize that the functions in this
 * module do *NOT* incorporate higher-level type system features like typedef.
 * For example, you could have C code like this:
 *
 *        typedef  int  *intptr;
 *       
 * In this case, a SwigType of type 'intptr' will be treated as a simple type and
 * functions like SwigType_ispointer() will evaluate as false.  It is strongly
 * advised that developers use the TypeSys_* interface to check types in a more
 * reliable manner.
 * ----------------------------------------------------------------------------- */


/* -----------------------------------------------------------------------------
 * NewSwigType()
 *
 * Constructs a new type object.   Eventually, it would be nice for this function
 * to accept an initial value in the form a C/C++ abstract type (currently unimplemented).
 * ----------------------------------------------------------------------------- */

#ifdef NEW
SwigType *NewSwigType(const_String_or_char_ptr initial) {
  return NewString(initial);
}

#endif

/* The next few functions are utility functions used in the construction and 
   management of types */

/* -----------------------------------------------------------------------------
 * static element_size()
 *
 * This utility function finds the size of a single type element in a type string.
 * Type elements are always delimited by periods, but may be nested with
 * parentheses.  A nested element is always handled as a single item.
 *
 * Returns the integer size of the element (which can be used to extract a 
 * substring, to chop the element off, or for other purposes).
 * ----------------------------------------------------------------------------- */

static int element_size(char *c) {
  int nparen;
  char *s = c;
  while (*c) {
    if (*c == '.') {
      c++;
      return (int) (c - s);
    } else if (*c == '(') {
      nparen = 1;
      c++;
      while (*c) {
	if (*c == '(')
	  nparen++;
	if (*c == ')') {
	  nparen--;
	  if (nparen == 0)
	    break;
	}
	c++;
      }
    }
    if (*c)
      c++;
  }
  return (int) (c - s);
}

/* -----------------------------------------------------------------------------
 * SwigType_del_element()
 *
 * Deletes one type element from the type.  
 * ----------------------------------------------------------------------------- */

SwigType *SwigType_del_element(SwigType *t) {
  int sz = element_size(Char(t));
  Delslice(t, 0, sz);
  return t;
}

/* -----------------------------------------------------------------------------
 * SwigType_pop()
 * 
 * Pop one type element off the type.
 * ----------------------------------------------------------------------------- */

SwigType *SwigType_pop(SwigType *t) {
  SwigType *result;
  char *c;
  int sz;

  c = Char(t);
  if (!*c)
    return 0;

  sz = element_size(c);
  result = NewStringWithSize(c, sz);
  Delslice(t, 0, sz);
  c = Char(t);
  if (*c == '.') {
    Delitem(t, 0);
  }
  return result;
}

/* -----------------------------------------------------------------------------
 * SwigType_parm()
 *
 * Returns the parameter of an operator as a string
 * ----------------------------------------------------------------------------- */

String *SwigType_parm(SwigType *t) {
  char *start, *c;
  int nparens = 0;

  c = Char(t);
  while (*c && (*c != '(') && (*c != '.'))
    c++;
  if (!*c || (*c == '.'))
    return 0;
  c++;
  start = c;
  while (*c) {
    if (*c == ')') {
      if (nparens == 0)
	break;
      nparens--;
    } else if (*c == '(') {
      nparens++;
    }
    c++;
  }
  return NewStringWithSize(start, (int) (c - start));
}

/* -----------------------------------------------------------------------------
 * SwigType_split()
 *
 * Splits a type into it's component parts and returns a list of string.
 * ----------------------------------------------------------------------------- */

List *SwigType_split(const SwigType *t) {
  String *item;
  List *list;
  char *c;
  int len;

  c = Char(t);
  list = NewList();
  while (*c) {
    len = element_size(c);
    item = NewStringWithSize(c, len);
    Append(list, item);
    Delete(item);
    c = c + len;
    if (*c == '.')
      c++;
  }
  return list;
}

/* -----------------------------------------------------------------------------
 * SwigType_parmlist()
 *
 * Splits a comma separated list of parameters into its component parts
 * The input is expected to contain the parameter list within () brackets
 * Returns 0 if no argument list in the input, ie there are no round brackets ()
 * Returns an empty List if there are no parameters in the () brackets
 * For example:
 *
 *     Foo(std::string,p.f().Bar<(int,double)>)
 *
 * returns 2 elements in the list:
 *    std::string
 *    p.f().Bar<(int,double)>
 * ----------------------------------------------------------------------------- */
 
List *SwigType_parmlist(const String *p) {
  String *item = 0;
  List *list;
  char *c;
  char *itemstart;
  int size;

  assert(p);
  c = Char(p);
  while (*c && (*c != '(') && (*c != '.'))
    c++;
  if (!*c)
    return 0;
  assert(*c != '.'); /* p is expected to contain sub elements of a type */
  c++;
  list = NewList();
  itemstart = c;
  while (*c) {
    if (*c == ',') {
      size = (int) (c - itemstart);
      item = NewStringWithSize(itemstart, size);
      Append(list, item);
      Delete(item);
      itemstart = c + 1;
    } else if (*c == '(') {
      int nparens = 1;
      c++;
      while (*c) {
	if (*c == '(')
	  nparens++;
	if (*c == ')') {
	  nparens--;
	  if (nparens == 0)
	    break;
	}
	c++;
      }
    } else if (*c == ')') {
      break;
    }
    if (*c)
      c++;
  }
  size = (int) (c - itemstart);
  if (size > 0) {
    item = NewStringWithSize(itemstart, size);
    Append(list, item);
  }
  Delete(item);
  return list;
}

/* -----------------------------------------------------------------------------
 *                                 Pointers
 *
 * SwigType_add_pointer()
 * SwigType_del_pointer()
 * SwigType_ispointer()
 *
 * Add, remove, and test if a type is a pointer.  The deletion and query
 * functions take into account qualifiers (if any).
 * ----------------------------------------------------------------------------- */

SwigType *SwigType_add_pointer(SwigType *t) {
  Insert(t, 0, "p.");
  return t;
}

SwigType *SwigType_del_pointer(SwigType *t) {
  char *c, *s;
  c = Char(t);
  s = c;
  /* Skip qualifiers, if any */
  if (strncmp(c, "q(", 2) == 0) {
    c = strchr(c, '.');
    assert(c);
    c++;
  }
  if (strncmp(c, "p.", 2)) {
    printf("Fatal error. SwigType_del_pointer applied to non-pointer.\n");
    abort();
  }
  Delslice(t, 0, (c - s) + 2);
  return t;
}

int SwigType_ispointer(SwigType *t) {
  char *c;
  if (!t)
    return 0;
  c = Char(t);
  /* Skip qualifiers, if any */
  if (strncmp(c, "q(", 2) == 0) {
    c = strchr(c, '.');
    if (!c)
      return 0;
    c++;
  }
  if (strncmp(c, "p.", 2) == 0) {
    return 1;
  }
  return 0;
}

/* -----------------------------------------------------------------------------
 *                                 References
 *
 * SwigType_add_reference()
 * SwigType_del_reference()
 * SwigType_isreference()
 *
 * Add, remove, and test if a type is a reference.  The deletion and query
 * functions take into account qualifiers (if any).
 * ----------------------------------------------------------------------------- */

SwigType *SwigType_add_reference(SwigType *t) {
  Insert(t, 0, "r.");
  return t;
}

SwigType *SwigType_del_reference(SwigType *t) {
  char *c = Char(t);
  int check = strncmp(c, "r.", 2);
  assert(check == 0);
  Delslice(t, 0, 2);
  return t;
}

int SwigType_isreference(SwigType *t) {
  char *c;
  if (!t)
    return 0;
  c = Char(t);
  if (strncmp(c, "r.", 2) == 0) {
    return 1;
  }
  return 0;
}

/* -----------------------------------------------------------------------------
 *                                  Qualifiers
 *
 * SwigType_add_qualifier()
 * SwigType_del_qualifier()
 * SwigType_is_qualifier()
 *
 * Adds type qualifiers like "const" and "volatile".   When multiple qualifiers
 * are added to a type, they are combined together into a single qualifier.
 * Repeated qualifications have no effect.  Moreover, the order of qualifications
 * is alphabetical---meaning that "const volatile" and "volatile const" are
 * stored in exactly the same way as "q(const volatile)".
 * ----------------------------------------------------------------------------- */

SwigType *SwigType_add_qualifier(SwigType *t, const_String_or_char_ptr qual) {
  char temp[256], newq[256];
  int sz, added = 0;
  char *q, *cqual;

  char *c = Char(t);
  cqual = Char(qual);

  if (!(strncmp(c, "q(", 2) == 0)) {
    sprintf(temp, "q(%s).", cqual);
    Insert(t, 0, temp);
    return t;
  }

  /* The type already has a qualifier on it.  In this case, we first check to
     see if the qualifier is already specified.  In that case do nothing.
     If it is a new qualifier, we add it to the qualifier list in alphabetical
     order */

  sz = element_size(c);
  strncpy(temp, c, (sz < 256) ? sz : 256);

  if (strstr(temp, cqual)) {
    /* Qualifier already added */
    return t;
  }

  /* Add the qualifier to the existing list. */

  strcpy(newq, "q(");
  q = temp + 2;
  q = strtok(q, " ).");
  while (q) {
    if (strcmp(cqual, q) < 0) {
      /* New qualifier is less that current qualifier.  We need to insert it */
      strcat(newq, cqual);
      strcat(newq, " ");
      strcat(newq, q);
      added = 1;
    } else {
      strcat(newq, q);
    }
    q = strtok(NULL, " ).");
    if (q) {
      strcat(newq, " ");
    }
  }
  if (!added) {
    strcat(newq, " ");
    strcat(newq, cqual);
  }
  strcat(newq, ").");
  Delslice(t, 0, sz);
  Insert(t, 0, newq);
  return t;
}

SwigType *SwigType_del_qualifier(SwigType *t) {
  char *c = Char(t);
  int check = strncmp(c, "q(", 2);
  assert(check == 0);
  Delslice(t, 0, element_size(c));
  return t;
}

int SwigType_isqualifier(SwigType *t) {
  char *c;
  if (!t)
    return 0;
  c = Char(t);
  if (strncmp(c, "q(", 2) == 0) {
    return 1;
  }
  return 0;
}

/* -----------------------------------------------------------------------------
 *                                Function Pointers
 * ----------------------------------------------------------------------------- */

int SwigType_isfunctionpointer(SwigType *t) {
  char *c;
  if (!t)
    return 0;
  c = Char(t);
  if (strncmp(c, "p.f(", 4) == 0) {
    return 1;
  }
  return 0;
}

/* -----------------------------------------------------------------------------
 * SwigType_functionpointer_decompose
 *
 * Decompose the function pointer into the parameter list and the return type
 * t - input and on completion contains the return type
 * returns the function's parameters
 * ----------------------------------------------------------------------------- */

SwigType *SwigType_functionpointer_decompose(SwigType *t) {
  String *p;
  assert(SwigType_isfunctionpointer(t));
  p = SwigType_pop(t);
  Delete(p);
  p = SwigType_pop(t);
  return p;
}

/* -----------------------------------------------------------------------------
 *                                Member Pointers
 *
 * SwigType_add_memberpointer()
 * SwigType_del_memberpointer()
 * SwigType_ismemberpointer()
 *
 * Add, remove, and test for C++ pointer to members.
 * ----------------------------------------------------------------------------- */

SwigType *SwigType_add_memberpointer(SwigType *t, const_String_or_char_ptr name) {
  String *temp = NewStringf("m(%s).", name);
  Insert(t, 0, temp);
  Delete(temp);
  return t;
}

SwigType *SwigType_del_memberpointer(SwigType *t) {
  char *c = Char(t);
  int check = strncmp(c, "m(", 2);
  assert(check == 0);
  Delslice(t, 0, element_size(c));
  return t;
}

int SwigType_ismemberpointer(SwigType *t) {
  char *c;
  if (!t)
    return 0;
  c = Char(t);
  if (strncmp(c, "m(", 2) == 0) {
    return 1;
  }
  return 0;
}

/* -----------------------------------------------------------------------------
 *                                    Arrays
 *
 * SwigType_add_array()
 * SwigType_del_array()
 * SwigType_isarray()
 *
 * Utility functions:
 *
 * SwigType_array_ndim()        - Calculate number of array dimensions.
 * SwigType_array_getdim()      - Get array dimension
 * SwigType_array_setdim()      - Set array dimension
 * SwigType_array_type()        - Return array type
 * SwigType_pop_arrays()        - Remove all arrays
 * ----------------------------------------------------------------------------- */

SwigType *SwigType_add_array(SwigType *t, const_String_or_char_ptr size) {
  char temp[512];
  strcpy(temp, "a(");
  strcat(temp, Char(size));
  strcat(temp, ").");
  Insert(t, 0, temp);
  return t;
}

SwigType *SwigType_del_array(SwigType *t) {
  char *c = Char(t);
  int check = strncmp(c, "a(", 2);
  assert(check == 0);
  Delslice(t, 0, element_size(c));
  return t;
}

int SwigType_isarray(SwigType *t) {
  char *c;
  if (!t)
    return 0;
  c = Char(t);
  if (strncmp(c, "a(", 2) == 0) {
    return 1;
  }
  return 0;
}
/*
 * SwigType_prefix_is_simple_1D_array
 *
 * Determine if the type is a 1D array type that is treated as a pointer within SWIG
 * eg Foo[], Foo[3] return true, but Foo[3][3], Foo*[], Foo*[3], Foo**[] return false
 */
int SwigType_prefix_is_simple_1D_array(SwigType *t) {
  char *c = Char(t);

  if (c && (strncmp(c, "a(", 2) == 0)) {
    c = strchr(c, '.');
    c++;
    return (*c == 0);
  }
  return 0;
}


/* Remove all arrays */
SwigType *SwigType_pop_arrays(SwigType *t) {
  String *ta;
  assert(SwigType_isarray(t));
  ta = NewStringEmpty();
  while (SwigType_isarray(t)) {
    SwigType *td = SwigType_pop(t);
    Append(ta, td);
    Delete(td);
  }
  return ta;
}

/* Return number of array dimensions */
int SwigType_array_ndim(SwigType *t) {
  int ndim = 0;
  char *c = Char(t);

  while (c && (strncmp(c, "a(", 2) == 0)) {
    c = strchr(c, '.');
    c++;
    ndim++;
  }
  return ndim;
}

/* Get nth array dimension */
String *SwigType_array_getdim(SwigType *t, int n) {
  char *c = Char(t);
  while (c && (strncmp(c, "a(", 2) == 0) && (n > 0)) {
    c = strchr(c, '.');
    c++;
    n--;
  }
  if (n == 0) {
    String *dim = SwigType_parm(c);
    if (SwigType_istemplate(dim)) {
      String *ndim = SwigType_namestr(dim);
      Delete(dim);
      dim = ndim;
    }

    return dim;
  }

  return 0;
}

/* Replace nth array dimension */
void SwigType_array_setdim(SwigType *t, int n, const_String_or_char_ptr rep) {
  String *result = 0;
  char temp;
  char *start;
  char *c = Char(t);

  start = c;
  if (strncmp(c, "a(", 2))
    abort();

  while (c && (strncmp(c, "a(", 2) == 0) && (n > 0)) {
    c = strchr(c, '.');
    c++;
    n--;
  }
  if (n == 0) {
    temp = *c;
    *c = 0;
    result = NewString(start);
    Printf(result, "a(%s)", rep);
    *c = temp;
    c = strchr(c, '.');
    Append(result, c);
  }
  Clear(t);
  Append(t, result);
  Delete(result);
}

/* Return base type of an array */
SwigType *SwigType_array_type(SwigType *ty) {
  SwigType *t;
  t = Copy(ty);
  while (SwigType_isarray(t)) {
    Delete(SwigType_pop(t));
  }
  return t;
}


/* -----------------------------------------------------------------------------
 *                                    Functions
 *
 * SwigType_add_function()
 * SwigType_del_function()
 * SwigType_isfunction()
 * SwigType_pop_function()
 *
 * Add, remove, and test for function types.
 * ----------------------------------------------------------------------------- */

/* Returns the function type, t, constructed from the parameters, parms */
SwigType *SwigType_add_function(SwigType *t, ParmList *parms) {
  String *pstr;
  Parm *p;

  Insert(t, 0, ").");
  pstr = NewString("f(");
  p = parms;
  for (p = parms; p; p = nextSibling(p)) {
    if (p != parms)
      Putc(',', pstr);
    Append(pstr, Getattr(p, "type"));
  }
  Insert(t, 0, pstr);
  Delete(pstr);
  return t;
}

SwigType *SwigType_pop_function(SwigType *t) {
  SwigType *f = 0;
  SwigType *g = 0;
  char *c = Char(t);
  if (strncmp(c, "q(", 2) == 0) {
    f = SwigType_pop(t);
    c = Char(t);
  }
  if (strncmp(c, "f(", 2)) {
    printf("Fatal error. SwigType_pop_function applied to non-function.\n");
    abort();
  }
  g = SwigType_pop(t);
  if (f)
    SwigType_push(g, f);
  Delete(f);
  return g;
}

int SwigType_isfunction(SwigType *t) {
  char *c;
  if (!t) {
    return 0;
  }
  c = Char(t);
  if (strncmp(c, "q(", 2) == 0) {
    /* Might be a 'const' function.  Try to skip over the 'const' */
    c = strchr(c, '.');
    if (c)
      c++;
    else
      return 0;
  }
  if (strncmp(c, "f(", 2) == 0) {
    return 1;
  }
  return 0;
}

ParmList *SwigType_function_parms(SwigType *t) {
  List *l = SwigType_parmlist(t);
  Hash *p, *pp = 0, *firstp = 0;
  Iterator o;

  for (o = First(l); o.item; o = Next(o)) {
    p = NewParm(o.item, 0);
    if (!firstp)
      firstp = p;
    if (pp) {
      set_nextSibling(pp, p);
      Delete(p);
    }
    pp = p;
  }
  Delete(l);
  return firstp;
}

int SwigType_isvarargs(const SwigType *t) {
  if (Strcmp(t, "v(...)") == 0)
    return 1;
  return 0;
}

/* -----------------------------------------------------------------------------
 *                                    Templates
 *
 * SwigType_add_template()
 *
 * Template handling.
 * ----------------------------------------------------------------------------- */

/* -----------------------------------------------------------------------------
 * SwigType_add_template()
 *
 * Adds a template to a type.   This template is encoded in the SWIG type
 * mechanism and produces a string like this:
 *
 *  vector<int *> ----> "vector<(p.int)>"
 * ----------------------------------------------------------------------------- */

SwigType *SwigType_add_template(SwigType *t, ParmList *parms) {
  Parm *p;

  Append(t, "<(");
  p = parms;
  for (p = parms; p; p = nextSibling(p)) {
    String *v;
    if (Getattr(p, "default"))
      continue;
    if (p != parms)
      Append(t, ",");
    v = Getattr(p, "value");
    if (v) {
      Append(t, v);
    } else {
      Append(t, Getattr(p, "type"));
    }
  }
  Append(t, ")>");
  return t;
}


/* -----------------------------------------------------------------------------
 * SwigType_templateprefix()
 *
 * Returns the prefix before the first template definition.
 * For example:
 *
 *     Foo<(p.int)>::bar
 *
 * returns "Foo"
 * ----------------------------------------------------------------------------- */

String *SwigType_templateprefix(const SwigType *t) {
  const char *s = Char(t);
  const char *c = strstr(s, "<(");
  return c ? NewStringWithSize(s, c - s) : NewString(s);
}

/* -----------------------------------------------------------------------------
 * SwigType_templatesuffix()
 *
 * Returns text after a template substitution.  Used to handle scope names
 * for example:
 *
 *        Foo<(p.int)>::bar
 *
 * returns "::bar"
 * ----------------------------------------------------------------------------- */

String *SwigType_templatesuffix(const SwigType *t) {
  const char *c;
  c = Char(t);
  while (*c) {
    if ((*c == '<') && (*(c + 1) == '(')) {
      int nest = 1;
      c++;
      while (*c && nest) {
	if (*c == '<')
	  nest++;
	if (*c == '>')
	  nest--;
	c++;
      }
      return NewString(c);
    }
    c++;
  }
  return NewStringEmpty();
}

/* -----------------------------------------------------------------------------
 * SwigType_templateargs()
 *
 * Returns the template arguments
 * For example:
 *
 *     Foo<(p.int)>::bar
 *
 * returns "<(p.int)>"
 * ----------------------------------------------------------------------------- */

String *SwigType_templateargs(const SwigType *t) {
  const char *c;
  const char *start;
  c = Char(t);
  while (*c) {
    if ((*c == '<') && (*(c + 1) == '(')) {
      int nest = 1;
      start = c;
      c++;
      while (*c && nest) {
	if (*c == '<')
	  nest++;
	if (*c == '>')
	  nest--;
	c++;
      }
      return NewStringWithSize(start, c - start);
    }
    c++;
  }
  return 0;
}

/* -----------------------------------------------------------------------------
 * SwigType_istemplate()
 *
 * Tests a type to see if it includes template parameters
 * ----------------------------------------------------------------------------- */

int SwigType_istemplate(const SwigType *t) {
  char *ct = Char(t);
  ct = strstr(ct, "<(");
  if (ct && (strstr(ct + 2, ")>")))
    return 1;
  return 0;
}

/* -----------------------------------------------------------------------------
 * SwigType_base()
 *
 * This function returns the base of a type.  For example, if you have a
 * type "p.p.int", the function would return "int".
 * ----------------------------------------------------------------------------- */

SwigType *SwigType_base(const SwigType *t) {
  char *c;
  char *lastop = 0;
  c = Char(t);

  lastop = c;

  /* Search for the last type constructor separator '.' */
  while (*c) {
    if (*c == '.') {
      if (*(c + 1)) {
	lastop = c + 1;
      }
      c++;
      continue;
    }
    if (*c == '<') {
      /* Skip over template---it's part of the base name */
      int ntemp = 1;
      c++;
      while ((*c) && (ntemp > 0)) {
	if (*c == '>')
	  ntemp--;
	else if (*c == '<')
	  ntemp++;
	c++;
      }
      if (ntemp)
	break;
      continue;
    }
    if (*c == '(') {
      /* Skip over params */
      int nparen = 1;
      c++;
      while ((*c) && (nparen > 0)) {
	if (*c == '(')
	  nparen++;
	else if (*c == ')')
	  nparen--;
	c++;
      }
      if (nparen)
	break;
      continue;
    }
    c++;
  }
  return NewString(lastop);
}

/* -----------------------------------------------------------------------------
 * SwigType_prefix()
 *
 * Returns the prefix of a datatype.  For example, the prefix of the
 * type "p.p.int" is "p.p.".
 * ----------------------------------------------------------------------------- */

String *SwigType_prefix(const SwigType *t) {
  char *c, *d;
  String *r = 0;

  c = Char(t);
  d = c + strlen(c);

  /* Check for a type constructor */
  if ((d > c) && (*(d - 1) == '.'))
    d--;

  while (d > c) {
    d--;
    if (*d == '>') {
      int nest = 1;
      d--;
      while ((d > c) && (nest)) {
	if (*d == '>')
	  nest++;
	if (*d == '<')
	  nest--;
	d--;
      }
    }
    if (*d == ')') {
      /* Skip over params */
      int nparen = 1;
      d--;
      while ((d > c) && (nparen)) {
	if (*d == ')')
	  nparen++;
	if (*d == '(')
	  nparen--;
	d--;
      }
    }

    if (*d == '.') {
      char t = *(d + 1);
      *(d + 1) = 0;
      r = NewString(c);
      *(d + 1) = t;
      return r;
    }
  }
  return NewStringEmpty();
}

/* -----------------------------------------------------------------------------
 * SwigType_strip_qualifiers()
 * 
 * Strip all qualifiers from a type and return a new type
 * ----------------------------------------------------------------------------- */

SwigType *SwigType_strip_qualifiers(SwigType *t) {
  static Hash *memoize_stripped = 0;
  SwigType *r;
  List *l;
  Iterator ei;

  if (!memoize_stripped)
    memoize_stripped = NewHash();
  r = Getattr(memoize_stripped, t);
  if (r)
    return Copy(r);

  l = SwigType_split(t);
  r = NewStringEmpty();

  for (ei = First(l); ei.item; ei = Next(ei)) {
    if (SwigType_isqualifier(ei.item))
      continue;
    Append(r, ei.item);
  }
  Delete(l);
  {
    String *key, *value;
    key = Copy(t);
    value = Copy(r);
    Setattr(memoize_stripped, key, value);
    Delete(key);
    Delete(value);
  }
  return r;
}