/* -----------------------------------------------------------------------------
 * 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.
 *
 * misc.c
 *
 * Miscellaneous functions that don't really fit anywhere else.
 * ----------------------------------------------------------------------------- */

#include "swig.h"
#include <assert.h>
#include <errno.h>
#include <ctype.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>

#ifdef _WIN32
#include <direct.h>
#ifndef S_ISDIR
/* Formatting changed/corrected in clang-format-17 */
/* clang-format off */
#define S_ISDIR(mode) (((mode) & S_IFDIR) == S_IFDIR)
/* clang-format on */
#endif
#endif

static char *fake_version = 0;

/* -----------------------------------------------------------------------------
 * Swig_copy_string()
 *
 * Duplicate a NULL-terminate string given as a char *.
 * ----------------------------------------------------------------------------- */

char *Swig_copy_string(const char *s) {
  char *c = 0;
  if (s) {
    c = (char *)Malloc(strlen(s) + 1);
    strcpy(c, s);
  }
  return c;
}

/* -----------------------------------------------------------------------------
 * Swig_set_fakeversion()
 *
 * Version string override
 * ----------------------------------------------------------------------------- */

void Swig_set_fakeversion(const char *version) {
  fake_version = Swig_copy_string(version);
}

/* -----------------------------------------------------------------------------
 * Swig_package_version()
 *
 * Return the package string containing the version number
 * ----------------------------------------------------------------------------- */

const char *Swig_package_version(void) {
  return fake_version ? fake_version : PACKAGE_VERSION;
}

/* -----------------------------------------------------------------------------
 * Swig_package_version_hex()
 *
 * Return the package version in hex format "0xAABBCC" such as "0x040200" for 4.2.0
 * ----------------------------------------------------------------------------- */

String *Swig_package_version_hex(void) {
  String *package_version = NewString(Swig_package_version());
  char *token = strtok(Char(package_version), ".");
  String *vers = NewString("SWIG_VERSION 0x");
  int count = 0;
  while (token) {
    int len = (int)strlen(token);
    assert(len == 1 || len == 2);
    Printf(vers, "%s%s", (len == 1) ? "0" : "", token);
    token = strtok(NULL, ".");
    count++;
  }
  Delete(package_version);
  assert(count == 3); /* Check version format is correct */
  return vers;
}

/* -----------------------------------------------------------------------------
 * Swig_obligatory_macros()
 *
 * Generates the SWIG_VERSION and SWIGXXX macros where XXX is the target language
 * name (must be provided uppercase).
 * ----------------------------------------------------------------------------- */

void Swig_obligatory_macros(String *f_runtime, const char *language) {
  String *version_hex = Swig_package_version_hex();
  Printf(f_runtime, "\n\n");
  Printf(f_runtime, "#define %s\n", version_hex);
  Printf(f_runtime, "#define SWIG%s\n", language);
  Delete(version_hex);
}

/* -----------------------------------------------------------------------------
 * Swig_banner()
 *
 * Emits the SWIG identifying banner for the C/C++ wrapper file.
 * ----------------------------------------------------------------------------- */

void Swig_banner(File *f) {
  Printf(f, "/* ----------------------------------------------------------------------------\n");
  Swig_banner_target_lang(f, " *");
  Printf(f, " * ----------------------------------------------------------------------------- */\n");
}

/* -----------------------------------------------------------------------------
 * Swig_banner_target_lang()
 *
 * Emits a SWIG identifying banner in the target language
 * ----------------------------------------------------------------------------- */

void Swig_banner_target_lang(File *f, const_String_or_char_ptr commentchar) {
  Printf(f, "%s This file was automatically generated by SWIG (https://www.swig.org).\n", commentchar);
  Printf(f, "%s Version %s\n", commentchar, Swig_package_version());
  Printf(f, "%s\n", commentchar);
  Printf(f, "%s Do not make changes to this file unless you know what you are doing - modify\n", commentchar);
  Printf(f, "%s the SWIG interface file instead.\n", commentchar);
}

/* -----------------------------------------------------------------------------
 * Swig_strip_c_comments()
 *
 * Return a new string with C comments stripped from the input string. NULL is
 * returned if there aren't any comments.
 * ----------------------------------------------------------------------------- */

String *Swig_strip_c_comments(const String *s) {
  const char *c = Char(s);
  const char *comment_begin = 0;
  const char *comment_end = 0;
  String *stripped = 0;

  while (*c) {
    if (!comment_begin && *c == '/') {
      ++c;
      if (!*c)
        break;
      if (*c == '*')
        comment_begin = c - 1;
    } else if (comment_begin && !comment_end && *c == '*') {
      ++c;
      if (*c == '/') {
        comment_end = c;
        break;
      }
    }
    ++c;
  }

  if (comment_begin && comment_end) {
    int size = (int)(comment_begin - Char(s));
    String *stripmore = 0;
    stripped = NewStringWithSize(s, size);
    Printv(stripped, comment_end + 1, NIL);
    do {
      stripmore = Swig_strip_c_comments(stripped);
      if (stripmore) {
        Delete(stripped);
        stripped = stripmore;
      }
    } while (stripmore);
  }
  return stripped;
}

/* -----------------------------------------------------------------------------
 * is_directory()
 * ----------------------------------------------------------------------------- */
static int is_directory(String *directory) {
  int last = Len(directory) - 1;
  int statres;
  struct stat st;
  char *dir = Char(directory);
  if (dir[last] == SWIG_FILE_DELIMITER[0]) {
    /* remove trailing slash - can cause S_ISDIR to fail on Windows, at least */
    dir[last] = 0;
    statres = stat(dir, &st);
    dir[last] = SWIG_FILE_DELIMITER[0];
  } else {
    statres = stat(dir, &st);
  }
  return (statres == 0 && S_ISDIR(st.st_mode));
}

/* -----------------------------------------------------------------------------
 * Swig_new_subdirectory()
 *
 * Create the subdirectory only if the basedirectory already exists as a directory.
 * basedirectory can be empty to indicate current directory but not NULL.
 * ----------------------------------------------------------------------------- */

String *Swig_new_subdirectory(String *basedirectory, String *subdirectory) {
  String *error = 0;
  int current_directory = Len(basedirectory) == 0;

  if (current_directory || is_directory(basedirectory)) {
    Iterator it;
    String *dir = NewString(basedirectory);
    List *subdirs = Split(subdirectory, SWIG_FILE_DELIMITER[0], INT_MAX);

    for (it = First(subdirs); it.item; it = Next(it)) {
      int result;
      String *subdirectory = it.item;
      Printf(dir, "%s", subdirectory);
#ifdef _WIN32
      result = _mkdir(Char(dir));
#else
      result = mkdir(Char(dir), 0777);
#endif
      if (result != 0 && errno != EEXIST) {
        error = NewStringf("Cannot create directory %s: %s", dir, strerror(errno));
        break;
      }
      if (!is_directory(dir)) {
        error = NewStringf("Cannot create directory %s: it may already exist but not be a directory", dir);
        break;
      }
      Printf(dir, SWIG_FILE_DELIMITER);
    }
  } else {
    error = NewStringf("Cannot create subdirectory %s under the base directory %s. Either the base does not exist as a directory or it is not readable.",
                       subdirectory,
                       basedirectory);
  }
  return error;
}

/* -----------------------------------------------------------------------------
 * Swig_filename_correct()
 *
 * Corrects filename paths by removing duplicate delimiters and on non-unix
 * systems use the correct delimiter across the whole name.
 * ----------------------------------------------------------------------------- */

void Swig_filename_correct(String *filename) {
  int network_path = 0;
  if (Len(filename) >= 2) {
    const char *fname = Char(filename);
    if (fname[0] == '\\' && fname[1] == '\\')
      network_path = 1;
    if (fname[0] == '/' && fname[1] == '/')
      network_path = 1;
  }
#if defined(_WIN32)
  /* accept Unix path separator on non-Unix systems */
  Replaceall(filename, "/", SWIG_FILE_DELIMITER);
#endif
#if defined(__CYGWIN__)
  /* accept Windows path separator in addition to Unix path separator */
  Replaceall(filename, "\\", SWIG_FILE_DELIMITER);
#endif
  /* remove all duplicate file name delimiters */
  while (Replaceall(filename, SWIG_FILE_DELIMITER SWIG_FILE_DELIMITER, SWIG_FILE_DELIMITER)) {
  }
  /* Network paths can start with a double slash on Windows - unremove the duplicate slash we just removed */
  if (network_path)
    Insert(filename, 0, SWIG_FILE_DELIMITER);
}

/* -----------------------------------------------------------------------------
 * Swig_filename_escape()
 *
 * Escapes backslashes in filename - for Windows
 * ----------------------------------------------------------------------------- */

String *Swig_filename_escape(String *filename) {
  String *adjusted_filename = Copy(filename);
  Swig_filename_correct(adjusted_filename);
#if defined(_WIN32) /* Note not on Cygwin else filename is displayed with double '/' */
  Replaceall(adjusted_filename, "\\", "\\\\");
#endif
  return adjusted_filename;
}

/* -----------------------------------------------------------------------------
 * Swig_filename_escape()
 *
 * Escapes spaces in filename - for Makefiles
 * ----------------------------------------------------------------------------- */

String *Swig_filename_escape_space(String *filename) {
  String *adjusted_filename = Copy(filename);
  Swig_filename_correct(adjusted_filename);
  Replaceall(adjusted_filename, " ", "\\ ");
  return adjusted_filename;
}

/* -----------------------------------------------------------------------------
 * Swig_filename_unescape()
 *
 * Remove double backslash escaping in filename - for Windows
 * ----------------------------------------------------------------------------- */

void Swig_filename_unescape(String *filename) {
  (void)filename;
#if defined(_WIN32)
  Replaceall(filename, "\\\\", "\\");
#endif
}

/* -----------------------------------------------------------------------------
 * Swig_storage_isextern()
 *
 * Determine if the storage class specifier is extern (but not externc)
 * ----------------------------------------------------------------------------- */

int Swig_storage_isextern(Node *n) {
  const String *storage = Getattr(n, "storage");
  return storage ? Strcmp(storage, "extern") == 0 || Strncmp(storage, "extern ", 7) == 0 : 0;
}

/* -----------------------------------------------------------------------------
 * Swig_storage_isexternc()
 *
 * Determine if the storage class specifier is externc (but not plain extern)
 * ----------------------------------------------------------------------------- */

int Swig_storage_isexternc(Node *n) {
  const String *storage = Getattr(n, "storage");
  return storage ? Strcmp(storage, "externc") == 0 || Strncmp(storage, "externc ", 8) == 0 : 0;
}

/* -----------------------------------------------------------------------------
 * Swig_storage_isstatic_custom()
 *
 * Determine if the storage class specifier is static
 * ----------------------------------------------------------------------------- */

int Swig_storage_isstatic_custom(Node *n, const_String_or_char_ptr storage_name) {
  const String *storage = Getattr(n, storage_name);
  return storage ? Strncmp(storage, "static", 6) == 0 : 0;
}

/* -----------------------------------------------------------------------------
 * Swig_storage_isstatic()
 *
 * Determine if the storage class specifier is static
 * ----------------------------------------------------------------------------- */

int Swig_storage_isstatic(Node *n) {
  return Swig_storage_isstatic_custom(n, "storage");
}

/* -----------------------------------------------------------------------------
 * Swig_string_escape()
 *
 * Takes a string object and produces a string with escape codes added to it.
 * Octal escaping is used.  The result is used for literal strings and characters
 * in C/C++ and also Java, Python, R and Ruby.
 *
 * The result is suitable for wrapping in single or double quotes to form a
 * character or string literal.
 *
 * Note that it's not safe to concatenate the results of escaping two strings
 * - you need to concatenate first and escape second.  The problem case is
 * when the first string ends with a character which gets escaped using one or
 * two octal digits and the second string starts with a character which is an
 * octal digit.
 * ----------------------------------------------------------------------------- */

String *Swig_string_escape(String *s) {
  String *ns = NewStringEmpty();
  int c = Getc(s);
  while (c != EOF) {
    if (c == '\n') {
      Printf(ns, "\\n");
    } else if (c == '\r') {
      Printf(ns, "\\r");
    } else if (c == '\t') {
      Printf(ns, "\\t");
    } else if (c == '\\') {
      Printf(ns, "\\\\");
    } else if (c == '\'') {
      Printf(ns, "\\'");
    } else if (c == '\"') {
      Printf(ns, "\\\"");
    } else if (c >= 32 && c < 127) {
      Putc(c, ns);
    } else {
      int next_c = Getc(s);
      assert(c >= 0);
      if (next_c >= '0' && next_c < '8') {
        /* We need to emit 3 octal digits. */
        Printf(ns, "\\%03o", c);
      } else {
        Printf(ns, "\\%o", c);
      }
      c = next_c;
      continue;
    }
    c = Getc(s);
  }
  return ns;
}

/* -----------------------------------------------------------------------------
 * Swig_string_csharpescape()
 *
 * Takes a string object and produces a string with escape codes added to it
 * suitable for use as a C# string or character literal.
 * ----------------------------------------------------------------------------- */

static String *Swig_string_csharpescape(String *s) {
  String *ns;
  int c;
  ns = NewStringEmpty();

  while ((c = Getc(s)) != EOF) {
    if (c == '\n') {
      Printf(ns, "\\n");
    } else if (c == '\r') {
      Printf(ns, "\\r");
    } else if (c == '\t') {
      Printf(ns, "\\t");
    } else if (c == '\\') {
      Printf(ns, "\\\\");
    } else if (c == '\'') {
      Printf(ns, "\\'");
    } else if (c == '\"') {
      Printf(ns, "\\\"");
    } else if (c >= 32 && c < 127) {
      Putc(c, ns);
    } else {
      assert(c >= 0);
      // Emit 4 hex digits in case the next character is a hex digit.
      Printf(ns, "\\x%04X", c);
    }
  }
  return ns;
}

/* -----------------------------------------------------------------------------
 * Swig_string_goescape()
 *
 * Takes a string object and produces a string with escape codes added to it
 * suitable for use as a Go string or character literal.
 * ----------------------------------------------------------------------------- */

static String *Swig_string_goescape(String *s) {
  String *ns;
  int c;
  ns = NewStringEmpty();

  while ((c = Getc(s)) != EOF) {
    if (c == '\n') {
      Printf(ns, "\\n");
    } else if (c == '\r') {
      Printf(ns, "\\r");
    } else if (c == '\t') {
      Printf(ns, "\\t");
    } else if (c == '\\') {
      Printf(ns, "\\\\");
    } else if (c >= 32 && c < 127 && c != '\'' && c != '"') {
      Putc(c, ns);
    } else {
      // In Go, \' isn't valid in a double quoted string, while \" isn't valid
      // in a single quoted rune, so to avoid needing two different escaping
      // functions we always escape both using hex escapes.
      assert(c >= 0);
      Printf(ns, "\\x%02x", c);
    }
  }
  return ns;
}

/* -----------------------------------------------------------------------------
 * Swig_string_upper()
 *
 * Takes a string object and returns a copy that is uppercase
 * ----------------------------------------------------------------------------- */

String *Swig_string_upper(String *s) {
  String *ns;
  int c;
  ns = NewStringEmpty();

  Seek(s, 0, SEEK_SET);
  while ((c = Getc(s)) != EOF) {
    Putc(toupper(c), ns);
  }
  return ns;
}

/* -----------------------------------------------------------------------------
 * Swig_string_lower()
 *
 * Takes a string object and returns a copy that is lowercase
 * ----------------------------------------------------------------------------- */

String *Swig_string_lower(String *s) {
  String *ns;
  int c;
  ns = NewStringEmpty();

  Seek(s, 0, SEEK_SET);
  while ((c = Getc(s)) != EOF) {
    Putc(tolower(c), ns);
  }
  return ns;
}

/* -----------------------------------------------------------------------------
 * Swig_string_title()
 *
 * Takes a string object and returns a copy that is lowercase with first letter
 * capitalized
 * ----------------------------------------------------------------------------- */

String *Swig_string_title(String *s) {
  String *ns;
  int first = 1;
  int c;
  ns = NewStringEmpty();

  Seek(s, 0, SEEK_SET);
  while ((c = Getc(s)) != EOF) {
    Putc(first ? toupper(c) : tolower(c), ns);
    first = 0;
  }
  return ns;
}

/* -----------------------------------------------------------------------------
 * Swig_string_ccase()
 *
 * Takes a string object and returns a copy that is lowercase with the first
 * letter capitalized and the one following '_', which are removed.
 *
 *      camel_case -> CamelCase
 *      camelCase  -> CamelCase
 * ----------------------------------------------------------------------------- */

static String *Swig_string_ccase(String *s) {
  String *ns;
  int first = 1;
  int c;
  ns = NewStringEmpty();

  Seek(s, 0, SEEK_SET);
  while ((c = Getc(s)) != EOF) {
    if (c == '_') {
      first = 1;
      continue;
    }
    Putc(first ? toupper(c) : c, ns);
    first = 0;
  }
  return ns;
}

/* -----------------------------------------------------------------------------
 * Swig_string_lccase()
 *
 * Takes a string object and returns a copy with the character after
 * each '_' capitalised, and the '_' removed.  The first character is
 * also forced to lowercase.
 *
 *      camel_case -> camelCase
 *      CamelCase  -> camelCase
 * ----------------------------------------------------------------------------- */

static String *Swig_string_lccase(String *s) {
  String *ns;
  int first = 1;
  int after_underscore = 0;
  int c;
  ns = NewStringEmpty();

  Seek(s, 0, SEEK_SET);
  while ((c = Getc(s)) != EOF) {
    if (c == '_') {
      after_underscore = 1;
      continue;
    }
    if (first) {
      Putc(tolower(c), ns);
      first = 0;
    } else {
      Putc(after_underscore ? toupper(c) : c, ns);
    }
    after_underscore = 0;
  }
  return ns;
}

/* -----------------------------------------------------------------------------
 * Swig_string_ucase()
 *
 * This is the reverse case of ccase, ie
 *
 *      CamelCase -> camel_case
 *      get2D     -> get_2d
 *      asFloat2  -> as_float2
 * ----------------------------------------------------------------------------- */

static String *Swig_string_ucase(String *s) {
  String *ns;
  int c;
  int lastC = 0;
  int nextC = 0;
  int underscore = 0;
  ns = NewStringEmpty();

  /* We insert a underscore when:
     1. Lower case char followed by upper case char
     getFoo > get_foo; getFOo > get_foo; GETFOO > getfoo
     2. Number preceded by char and not end of string
     get2D > get_2d; get22D > get_22d; GET2D > get_2d
     but:
     asFloat2 > as_float2
  */

  Seek(s, 0, SEEK_SET);

  while ((c = Getc(s)) != EOF) {
    nextC = Getc(s);
    Ungetc(nextC, s);
    if (isdigit(c) && isalpha(lastC) && nextC != EOF)
      underscore = 1;
    else if (isupper(c) && isalpha(lastC) && !isupper(lastC))
      underscore = 1;

    lastC = c;

    if (underscore) {
      Putc('_', ns);
      underscore = 0;
    }

    Putc(tolower(c), ns);
  }
  return ns;
}

/* -----------------------------------------------------------------------------
 * Swig_string_first_upper()
 *
 * Make the first character in the string uppercase, leave all the
 * rest the same.  This is used by the Ruby module to provide backwards
 * compatibility with the old way of naming classes and constants.  For
 * more info see the Ruby documentation.
 *
 *      firstUpper -> FirstUpper
 * ----------------------------------------------------------------------------- */

static String *Swig_string_first_upper(String *s) {
  String *ns = NewStringEmpty();
  char *cs = Char(s);
  if (cs && cs[0] != 0) {
    Putc(toupper((int)cs[0]), ns);
    Append(ns, cs + 1);
  }
  return ns;
}

/* -----------------------------------------------------------------------------
 * Swig_string_first_lower()
 *
 * Make the first character in the string lowercase, leave all the
 * rest the same.  This is used by the Ruby module to provide backwards
 * compatibility with the old way of naming classes and constants.  For
 * more info see the Ruby documentation.
 *
 *      firstLower -> FirstLower
 * ----------------------------------------------------------------------------- */

static String *Swig_string_first_lower(String *s) {
  String *ns = NewStringEmpty();
  char *cs = Char(s);
  if (cs && cs[0] != 0) {
    Putc(tolower((int)cs[0]), ns);
    Append(ns, cs + 1);
  }
  return ns;
}

/* -----------------------------------------------------------------------------
 * Swig_string_schemify()
 *
 * Replace underscores with dashes, to make identifiers look nice to Schemers.
 *
 *      under_scores -> under-scores
 * ----------------------------------------------------------------------------- */

static String *Swig_string_schemify(String *s) {
  String *ns = NewString(s);
  Replaceall(ns, "_", "-");
  return ns;
}

static String *string_mangle(String *s) {
  return Swig_name_mangle_string(s);
}

/* -----------------------------------------------------------------------------
 * Swig_scopename_split()
 *
 * Take a qualified name like "A::B::C" and splits off the last name.
 * In this case, returns "C" as last and "A::B" as prefix.
 * Always returns non NULL for last, but prefix may be NULL if there is no prefix.
 * ----------------------------------------------------------------------------- */

void Swig_scopename_split(const String *s, String **rprefix, String **rlast) {
  char *tmp = Char(s);
  char *c = tmp;
  char *cc = c;
  char *co = 0;
  if (!strstr(c, "::")) {
    *rprefix = 0;
    *rlast = Copy(s);
  }

  co = strstr(cc, "operator ");
  if (co) {
    if (co == cc) {
      *rprefix = 0;
      *rlast = Copy(s);
      return;
    } else {
      *rprefix = NewStringWithSize(cc, (int)(co - cc - 2));
      *rlast = NewString(co);
      return;
    }
  }
  while (*c) {
    if ((*c == ':') && (*(c + 1) == ':')) {
      cc = c;
      c += 2;
    } else {
      if (*c == '<') {
        int level = 1;
        c++;
        while (*c && level) {
          if (*c == '<')
            level++;
          if (*c == '>')
            level--;
          c++;
        }
      } else {
        c++;
      }
    }
  }

  if (cc != tmp) {
    *rprefix = NewStringWithSize(tmp, (int)(cc - tmp));
    *rlast = NewString(cc + 2);
    return;
  } else {
    *rprefix = 0;
    *rlast = Copy(s);
  }
}

/* -----------------------------------------------------------------------------
 * Swig_scopename_prefix()
 *
 * Take a qualified name like "A::B::C" and return the scope name.
 * In this case, "A::B".   Returns NULL if there is no base.
 * ----------------------------------------------------------------------------- */

String *Swig_scopename_prefix(const String *s) {
  char *tmp = Char(s);
  char *c = tmp;
  char *cc = c;
  char *co = 0;
  if (!strstr(c, "::"))
    return 0;
  co = strstr(cc, "operator ");

  if (co) {
    if (co == cc) {
      return 0;
    } else {
      String *prefix = NewStringWithSize(cc, (int)(co - cc - 2));
      return prefix;
    }
  }
  while (*c) {
    if ((*c == ':') && (*(c + 1) == ':')) {
      cc = c;
      c += 2;
    } else {
      if (*c == '<') {
        int level = 1;
        c++;
        while (*c && level) {
          if (*c == '<')
            level++;
          if (*c == '>')
            level--;
          c++;
        }
      } else {
        c++;
      }
    }
  }

  if (cc != tmp) {
    return NewStringWithSize(tmp, (int)(cc - tmp));
  } else {
    return 0;
  }
}

/* -----------------------------------------------------------------------------
 * Swig_scopename_last()
 *
 * Take a qualified name like "A::B::C" and returns the last.  In this
 * case, "C".
 * ----------------------------------------------------------------------------- */

String *Swig_scopename_last(const String *s) {
  char *tmp = Char(s);
  char *c = tmp;
  char *cc = c;
  char *co = 0;
  if (!strstr(c, "::"))
    return NewString(s);

  co = strstr(cc, "operator ");
  if (co) {
    return NewString(co);
  }

  while (*c) {
    if ((*c == ':') && (*(c + 1) == ':')) {
      c += 2;
      cc = c;
    } else {
      if (*c == '<') {
        int level = 1;
        c++;
        while (*c && level) {
          if (*c == '<')
            level++;
          if (*c == '>')
            level--;
          c++;
        }
      } else {
        c++;
      }
    }
  }
  return NewString(cc);
}

/* -----------------------------------------------------------------------------
 * Swig_scopename_first()
 *
 * Take a qualified name like "A::B::C" and returns the first scope name.
 * In this case, "A".   Returns NULL if there is no base.
 * ----------------------------------------------------------------------------- */

String *Swig_scopename_first(const String *s) {
  char *tmp = Char(s);
  char *c = tmp;
  char *co = 0;
  if (!strstr(c, "::"))
    return 0;

  co = strstr(c, "operator ");
  if (co) {
    if (co == c) {
      return 0;
    }
  } else {
    co = c + Len(s);
  }

  while (*c && (c != co)) {
    if ((*c == ':') && (*(c + 1) == ':')) {
      break;
    } else {
      if (*c == '<') {
        int level = 1;
        c++;
        while (*c && level) {
          if (*c == '<')
            level++;
          if (*c == '>')
            level--;
          c++;
        }
      } else {
        c++;
      }
    }
  }
  if (*c && (c != tmp)) {
    return NewStringWithSize(tmp, (int)(c - tmp));
  } else {
    return 0;
  }
}

/* -----------------------------------------------------------------------------
 * Swig_scopename_suffix()
 *
 * Take a qualified name like "A::B::C" and returns the suffix.
 * In this case, "B::C".   Returns NULL if there is no suffix.
 * ----------------------------------------------------------------------------- */

String *Swig_scopename_suffix(const String *s) {
  char *tmp = Char(s);
  char *c = tmp;
  char *co = 0;
  if (!strstr(c, "::"))
    return 0;

  co = strstr(c, "operator ");
  if (co) {
    if (co == c)
      return 0;
  }
  while (*c) {
    if ((*c == ':') && (*(c + 1) == ':')) {
      break;
    } else {
      if (*c == '<') {
        int level = 1;
        c++;
        while (*c && level) {
          if (*c == '<')
            level++;
          if (*c == '>')
            level--;
          c++;
        }
      } else {
        c++;
      }
    }
  }
  if (*c && (c != tmp)) {
    return NewString(c + 2);
  } else {
    return 0;
  }
}

/* -----------------------------------------------------------------------------
 * Swig_scopename_tolist()
 *
 * Take a qualified scope name like "A::B::C" and convert it to a list.
 * In this case, return a list of 3 elements "A", "B", "C".
 * Returns an empty list if the input is empty.
 * ----------------------------------------------------------------------------- */

List *Swig_scopename_tolist(const String *s) {
  List *scopes = NewList();
  String *name = Len(s) == 0 ? 0 : NewString(s);

  while (name) {
    String *last = 0;
    String *prefix = 0;
    Swig_scopename_split(name, &prefix, &last);
    Insert(scopes, 0, last);
    Delete(last);
    Delete(name);
    name = prefix;
  }
  Delete(name);
  return scopes;
}

/* -----------------------------------------------------------------------------
 * Swig_scopename_isvalid()
 *
 * Checks that s is a valid scopename (C++ namespace scope)
 * ----------------------------------------------------------------------------- */

int Swig_scopename_isvalid(const String *s) {
  List *scopes = Swig_scopename_tolist(s);
  int valid = 0;
  Iterator si;

  for (si = First(scopes); si.item; si = Next(si)) {
    String *subscope = si.item;
    valid = subscope && Len(subscope) > 0;
    if (valid)
      valid = Swig_symbol_isvalid(subscope);
    if (!valid)
      break;
  }
  return valid;
}

/* -----------------------------------------------------------------------------
 * Swig_scopename_check()
 *
 * Checks to see if a name is qualified with a scope name, examples:
 *   foo            -> 0
 *   ::foo          -> 1
 *   foo::bar       -> 1
 *   foo< ::bar >   -> 0
 * ----------------------------------------------------------------------------- */

int Swig_scopename_check(const String *s) {
  char *c = Char(s);
  char *co = strstr(c, "operator ");

  if (co) {
    if (co == c)
      return 0;
  }
  if (!strstr(c, "::"))
    return 0;
  while (*c) {
    if ((*c == ':') && (*(c + 1) == ':')) {
      return 1;
    } else {
      if (*c == '<') {
        int level = 1;
        c++;
        while (*c && level) {
          if (*c == '<')
            level++;
          if (*c == '>')
            level--;
          c++;
        }
      } else {
        c++;
      }
    }
  }
  return 0;
}

/* -----------------------------------------------------------------------------
 * Swig_string_command()
 *
 * Feature removed in SWIG 4.1.0.
 * ----------------------------------------------------------------------------- */

static String *Swig_string_command(String *s) {
  Swig_error("SWIG", Getline(s), "Command encoder no longer supported - use regex encoder instead, command:%s\n", s);
  Exit(EXIT_FAILURE);
  return 0;
}

/* -----------------------------------------------------------------------------
 * Swig_string_strip()
 *
 * Strip given prefix from identifiers
 *
 *  Printf(stderr,"%(strip:[wx])s","wxHello") -> Hello
 * ----------------------------------------------------------------------------- */

static String *Swig_string_strip(String *s) {
  String *ns;
  if (!Len(s)) {
    ns = NewString(s);
  } else {
    const char *cs = Char(s);
    const char *ce = Strchr(cs, ']');
    if (*cs != '[' || !ce) {
      ns = NewString(s);
    } else {
      String *fmt = NewStringf("%%.%ds", ce - cs - 1);
      String *prefix = NewStringf(fmt, cs + 1);
      if (0 == Strncmp(ce + 1, prefix, Len(prefix))) {
        ns = NewString(ce + 1 + Len(prefix));
      } else {
        ns = NewString(ce + 1);
      }
    }
  }
  return ns;
}

/* -----------------------------------------------------------------------------
 * Swig_string_rstrip()
 *
 * Strip given suffix from identifiers
 *
 *  Printf(stderr,"%(rstrip:[Cls])s","HelloCls") -> Hello
 * ----------------------------------------------------------------------------- */

static String *Swig_string_rstrip(String *s) {
  String *ns;
  int len = Len(s);
  if (!len) {
    ns = NewString(s);
  } else {
    const char *cs = Char(s);
    const char *ce = Strchr(cs, ']');
    if (*cs != '[' || !ce) {
      ns = NewString(s);
    } else {
      String *fmt = NewStringf("%%.%ds", ce - cs - 1);
      String *suffix = NewStringf(fmt, cs + 1);
      int suffix_len = Len(suffix);
      if (0 == Strncmp(cs + len - suffix_len, suffix, suffix_len)) {
        int copy_len = len - suffix_len - (int)(ce + 1 - cs);
        ns = NewStringWithSize(ce + 1, copy_len);
      } else {
        ns = NewString(ce + 1);
      }
    }
  }
  return ns;
}

/* -----------------------------------------------------------------------------
 * Swig_offset_string()
 *
 * Insert number tabs before each new line in s
 * ----------------------------------------------------------------------------- */

void Swig_offset_string(String *s, int number) {
  char *res, *p, *end, *start;
  /* count a number of lines in s */
  int lines = 1;
  int len = Len(s);
  if (len == 0)
    return;
  start = strchr(Char(s), '\n');
  while (start) {
    ++lines;
    start = strchr(start + 1, '\n');
  }
  /* do not count pending new line */
  if ((Char(s))[len - 1] == '\n')
    --lines;
  /* allocate a temporary storage for a padded string */
  res = (char *)Malloc(len + lines * number * 2 + 1);
  res[len + lines * number * 2] = 0;

  /* copy lines to res, prepending tabs to each line */
  p = res;                   /* output pointer */
  start = Char(s);           /* start of a current line */
  end = strchr(start, '\n'); /* end of a current line */
  while (end) {
    memset(p, ' ', number * 2);
    p += number * 2;
    memcpy(p, start, end - start + 1);
    p += end - start + 1;
    start = end + 1;
    end = strchr(start, '\n');
  }
  /* process the last line */
  if (*start) {
    memset(p, ' ', number * 2);
    p += number * 2;
    strcpy(p, start);
  }
  /* replace 's' contents with 'res' */
  Clear(s);
  Append(s, res);
  Free(res);
}

#ifdef HAVE_PCRE
#define PCRE2_CODE_UNIT_WIDTH 8
#include <pcre2.h>

static int split_regex_pattern_subst(String *s, String **pattern, String **subst, const char **input) {
  const char *pats, *pate;
  const char *subs, *sube;

  /* Locate the search pattern */
  const char *p = Char(s);
  if (*p++ != '/')
    goto err_out;
  pats = p;
  p = strchr(p, '/');
  if (!p)
    goto err_out;
  pate = p;

  /* Locate the substitution string */
  subs = ++p;
  p = strchr(p, '/');
  if (!p)
    goto err_out;
  sube = p;

  *pattern = NewStringWithSize(pats, (int)(pate - pats));
  *subst = NewStringWithSize(subs, (int)(sube - subs));
  *input = p + 1;
  return 1;

err_out:
  Swig_error("SWIG", Getline(s), "Invalid regex substitution: '%s'.\n", s);
  Exit(EXIT_FAILURE);
  return 0;
}

/* This function copies len characters from src to dst, possibly applying case conversions to them: if convertCase is 1, to upper case and if it is -1, to lower
 * case. If convertNextOnly is 1, only a single character is converted (and convertCase is reset), otherwise all of them are. */
static void copy_with_maybe_case_conversion(String *dst, const char *src, int len, int *convertCase, int convertNextOnly) {
  /* Deal with the trivial cases first. */
  if (!len)
    return;

  if (!*convertCase) {
    Write(dst, src, len);
    return;
  }

  /* If we must convert only the first character, do it and write the rest at once. */
  if (convertNextOnly) {
    int src_char = *src;
    Putc(*convertCase == 1 ? toupper(src_char) : tolower(src_char), dst);
    *convertCase = 0;
    if (len > 1) {
      Write(dst, src + 1, len - 1);
    }
  } else {
    /* We need to convert all characters. */
    int i;
    for (i = 0; i < len; i++, src++) {
      int src_char = *src;
      Putc(*convertCase == 1 ? toupper(src_char) : tolower(src_char), dst);
    }
  }
}

static String *replace_captures(int num_captures, const char *input, String *subst, size_t captures[], String *pattern, String *s) {
  int convertCase = 0, convertNextOnly = 0;
  String *result = NewStringEmpty();
  const char *p = Char(subst);

  while (*p) {
    /* Copy part without substitutions */
    const char *q = strchr(p, '\\');
    if (!q) {
      copy_with_maybe_case_conversion(result, p, (int)strlen(p), &convertCase, convertNextOnly);
      break;
    }
    copy_with_maybe_case_conversion(result, p, (int)(q - p), &convertCase, convertNextOnly);
    p = q + 1;

    /* Handle substitution */
    if (*p == '\0') {
      Putc('\\', result);
    } else if (isdigit((unsigned char)*p)) {
      int group = *p++ - '0';
      if (group < num_captures) {
        int l = (int)captures[group * 2], r = (int)captures[group * 2 + 1];
        if (l != -1) {
          copy_with_maybe_case_conversion(result, input + l, r - l, &convertCase, convertNextOnly);
        }
      } else {
        Swig_error("SWIG",
                   Getline(s),
                   "PCRE capture replacement failed while matching \"%s\" using \"%s\" - request for group %d is greater than the number of captures %d.\n",
                   Char(pattern),
                   input,
                   group,
                   num_captures - 1);
      }
    } else {
      /* Handle Perl-like case conversion escapes. */
      switch (*p) {
      case 'u':
        convertCase = 1;
        convertNextOnly = 1;
        break;
      case 'U':
        convertCase = 1;
        convertNextOnly = 0;
        break;
      case 'l':
        convertCase = -1;
        convertNextOnly = 1;
        break;
      case 'L':
        convertCase = -1;
        convertNextOnly = 0;
        break;
      case 'E':
        convertCase = 0;
        break;
      default:
        Swig_error("SWIG", Getline(s), "Unrecognized escape character '%c' in the replacement string \"%s\".\n", *p, Char(subst));
      }
      p++;
    }
  }

  return result;
}

/* -----------------------------------------------------------------------------
 * Swig_string_regex()
 *
 * Executes a regular expression substitution. For example:
 *
 *   Printf(stderr,"gsl%(regex:/GSL_(.*)_/\\1/)s", "GSL_Hello_") -> gslHello
 * ----------------------------------------------------------------------------- */
static String *Swig_string_regex(String *s) {
  const int pcre_options = 0;

  String *res = 0;
  pcre2_code *compiled_pat = 0;
  const char *input;
  PCRE2_UCHAR pcre_error[256];
  int pcre_errornum;
  size_t pcre_errorpos;
  String *pattern = 0, *subst = 0;
  size_t *captures = 0;
  pcre2_match_data *match_data = 0;
  if (split_regex_pattern_subst(s, &pattern, &subst, &input)) {
    int rc;

    compiled_pat = pcre2_compile((PCRE2_SPTR8)Char(pattern), PCRE2_ZERO_TERMINATED, pcre_options, &pcre_errornum, &pcre_errorpos, NULL);
    if (!compiled_pat) {
      pcre2_get_error_message(pcre_errornum, pcre_error, sizeof pcre_error);
      Swig_error("SWIG", Getline(s), "PCRE compilation failed: '%s' in '%s':%i.\n", pcre_error, Char(pattern), pcre_errorpos);
      Exit(EXIT_FAILURE);
    }
    match_data = pcre2_match_data_create_from_pattern(compiled_pat, NULL);
    rc = pcre2_match(compiled_pat, (PCRE2_SPTR8)input, PCRE2_ZERO_TERMINATED, 0, 0, match_data, NULL);
    captures = pcre2_get_ovector_pointer(match_data);
    if (rc >= 0) {
      res = replace_captures(rc, input, subst, captures, pattern, s);
    } else if (rc != PCRE2_ERROR_NOMATCH) {
      Swig_error("SWIG", Getline(s), "PCRE execution failed: error %d while matching \"%s\" using \"%s\".\n", rc, Char(pattern), input);
      Exit(EXIT_FAILURE);
    }
  }

  Delete(pattern);
  Delete(subst);
  pcre2_code_free(compiled_pat);
  pcre2_match_data_free(match_data);
  return res ? res : NewStringEmpty();
}

String *Swig_pcre_version(void) {
  int len = pcre2_config(PCRE2_CONFIG_VERSION, NULL);
  char *buf = Malloc(len);
  String *result;
  pcre2_config(PCRE2_CONFIG_VERSION, buf);
  result = NewStringf("PCRE2 Version: %s", buf);
  Free(buf);
  return result;
}

#else

String *Swig_string_regex(String *s) {
  Swig_error("SWIG", Getline(s), "PCRE regex support not enabled in this SWIG build.\n");
  Exit(EXIT_FAILURE);
  return 0;
}

String *Swig_pcre_version(void) {
  return NewStringf("PCRE not used");
}

#endif

/* ------------------------------------------------------------
 * Swig_is_generated_overload()
 * Check if the function is an automatically generated
 * overload created because a method has default parameters.
 * ------------------------------------------------------------ */

int Swig_is_generated_overload(Node *n) {
  Node *base_method = Getattr(n, "sym:overloaded");
  Node *default_args = Getattr(n, "defaultargs");
  return ((base_method != NULL) && (default_args != NULL) && (base_method == default_args));
}

/* -----------------------------------------------------------------------------
 * Swig_item_in_list()
 *
 * If the input item is in the list, return the item.
 * Note: uses DohCmp for comparisons so for a List of String *, Strcmp is ultimately
 * used for item comparisons to determine if a string is in the list.
 * ----------------------------------------------------------------------------- */

Node *Swig_item_in_list(List *list, const DOH *item) {
  Node *found_item = 0;
  if (list) {
    Iterator it;
    for (it = First(list); it.item; it = Next(it)) {
      if (DohCmp(item, it.item) == 0) {
        found_item = it.item;
        break;
      }
    }
  }
  return found_item;
}

/* -----------------------------------------------------------------------------
 * Swig_init()
 *
 * Initialize the SWIG core
 * ----------------------------------------------------------------------------- */

void Swig_init(void) {
  /* Set some useful string encoding methods */
  DohEncoding("escape", Swig_string_escape);
  DohEncoding("csharpescape", Swig_string_csharpescape);
  DohEncoding("goescape", Swig_string_goescape);
  DohEncoding("upper", Swig_string_upper);
  DohEncoding("lower", Swig_string_lower);
  DohEncoding("title", Swig_string_title);
  DohEncoding("ctitle", Swig_string_ccase);
  DohEncoding("lctitle", Swig_string_lccase);
  DohEncoding("utitle", Swig_string_ucase);
  DohEncoding("mangle", string_mangle);
  DohEncoding("command", Swig_string_command);
  DohEncoding("schemify", Swig_string_schemify);
  DohEncoding("strip", Swig_string_strip);
  DohEncoding("rstrip", Swig_string_rstrip);
  DohEncoding("regex", Swig_string_regex);

  /* aliases for the case encoders */
  DohEncoding("uppercase", Swig_string_upper);
  DohEncoding("lowercase", Swig_string_lower);
  DohEncoding("camelcase", Swig_string_ccase);
  DohEncoding("lowercamelcase", Swig_string_lccase);
  DohEncoding("undercase", Swig_string_ucase);
  DohEncoding("firstuppercase", Swig_string_first_upper);
  DohEncoding("firstlowercase", Swig_string_first_lower);

  /* Initialize typemaps */
  Swig_typemap_init();

  /* Initialize symbol table */
  Swig_symbol_init();

  /* Initialize type system */
  SwigType_typesystem_init();

  /* Initialize template system */
  SwigType_template_init();
}
