/* namespace.c: 
 *
 ****************************************************************
 * Copyright (C) 2003 Tom Lord
 * 
 * See the file "COPYING" for further information about
 * the copyright and warranty status of this work.
 */


#include "hackerlab/bugs/panic.h"
#include "hackerlab/fmt/cvt.h"
#include "hackerlab/char/char-class.h"
#include "hackerlab/char/str.h"
#include "hackerlab/sort/qsort.h"
#include "file-utils/libfsutils/safety.h"
#include "file-utils/libarch/my.h"
#include "file-utils/libarch/namespace.h"


/* __STDC__ prototypes for static functions */
static int arch_cmp_by_field (void * va, void * vb, void * vdata);
static int arch_lvl_cmp_by_field (void * va, void * vb, void * vdata);
static t_uchar * over_opt_archive_prefix (t_uchar * name);
static t_uchar * over_archive_name (t_uchar * in);
static t_uchar * over_basename (t_uchar * in);
static t_uchar * over_separator (t_uchar * in);
static t_uchar * over_version (t_uchar * in);
static t_uchar * over_patch_level (t_uchar * in);



int
arch_valid_id (t_uchar * id)
{
  t_uchar * non_empty_marker;

  /* This isn't exactly the same as larch's version --
   * it's a superset.   Both are wrong, really. :-)
   */

  while (char_is_alnum (*id)
         || char_is_space (*id)
         || (char_is_punct (*id) && (*id != '<')))
    ++id;

  if (*id != '<')
    return 0;

  ++id;

  non_empty_marker = id;

  while (char_is_alnum (*id)
         || (*id == '-')
         || (*id == '_')
         || (*id == '.'))
    ++id;

  if (id == non_empty_marker)
    return 0;

  if (*id != '@')
    return 0;

  ++id;

  non_empty_marker = id;

  while (char_is_alnum (*id)
         || (*id == '-')
         || (*id == '_')
         || (*id == '.'))
    ++id;

  if (*id != '>')
    return 0;

  ++id;

  if (*id)
    return 0;

  return 1;
}

int
arch_valid_archive_name (t_uchar * name)
{
  t_uchar * end;

  end = over_archive_name (name);

  if (!end || *end)
    return 0;
  else
    return 1;
}

int 
arch_valid_patch_level_name (t_uchar * name)
{
  t_uchar * lvl;

  if (!str_cmp (name, "base-0"))
    return 1;

  if (str_cmp_prefix ("patch-", name) && str_cmp_prefix ("version-", name) && str_cmp_prefix ("versionfix-", name))
    return 0;

  lvl = str_chr_index (name, '-');
  if (!lvl)
    return 0;

  ++lvl;

  if (!*lvl)
    return 0;

  while (char_is_digit (*lvl))
    ++lvl;

  if (*lvl)
    return 0;
  else
    return 1;
}

int 
arch_valid_config_name (t_uchar * name)
{
  return is_non_upwards_relative_path (name);
}


/*(c arch_valid_package_name)
 * int arch_valid_package_name (t_uchar * name,
 *                              enum arch_valid_package_name_archive archive_disposition,
 *                              enum arch_valid_package_name_types type,
 *                              int tolerant);
 * 
 * Return non-0 if `name' is a valid arch name of the sort described by 
 * the other arguments.
 * 
 * `archive_disposition' may be any of the values:
 * 
 * 	arch_no_archive		the name must not be fully qualified (must have no
 *				no archive name component)
 * 	
 * 	arch_maybe_archive	the name _may_ be fully qualified
 *	
 *	arch_req_archive	the name _must_ be fully qualified
 * 
 * 
 * `type' may be any of the values:
 * 
 * 	arch_req_category	the name must have at least a category
 * 
 * 	arch_req_package	the name must have a category and may 
 * 				have a branch label
 * 
 * 	arch_req_version	the name must have a version id
 *				(and therefore must have a category and
 *				may have a branch label)
 * 
 * 	arch_req_patch_level	the name must have a category, may have a 
 *				branch label, must have a version id, and
 *				must have a revision name
 * 
 * `tolerant':
 * 
 * 	if 0, then the name may not have any components beyond those 
 * 	required by `type'.  For example, if type is `arch_req_category',
 * 	then the name may not have a branch label or version id.
 * 
 * 	if 1, then the name may have additional components
 */
int
arch_valid_package_name (t_uchar * name,
                         enum arch_valid_package_name_archive archive_disposition,
                         enum arch_valid_package_name_types type,
                         int tolerant)
{
  int has_archive;
  int has_category;
  int has_branch;
  int has_version;
  int has_patch_level;
  t_uchar * next;

  has_archive = 0;
  has_category = 0;
  has_branch = 0;
  has_version = 0;
  has_patch_level = 0;


  next = over_opt_archive_prefix (name);

  has_archive = (next && (next != name));

  switch (archive_disposition)
    {
    case arch_maybe_archive:
      {
        if (!next)
          return 0;
        break;
      }

    case arch_req_archive:
      {
        if (!next || (next == name))
          return 0;
        break;
      }

    case arch_no_archive:
      {
        if (!next || (next != name))
          return 0;
        break;
      }
    }

  name = next;


  name = over_basename (name);
  if (!name)
    return 0;
  else
    has_category = 1;

  if (*name)
    {
      name = over_separator (name);
      if (!name)
        return 0;

      next = over_basename (name);
      if (next)
        {
          has_branch = 1;
          name = next;
          if (*name)
            {
              name = over_separator (name);
              if (!name)
                return 0;
            }
        }

      if (*name)
        {
          name = over_version (name);
          if (!name)
            return 0;
          has_version = 1;

          if (*name)
            {
              name = over_separator (name);
              if (!name)
                return 0;

              name = over_patch_level (name);
              if (!name || *name)
                return 0;

              has_patch_level = 1;
            }
        }
    }

  switch (type)
    {
    case arch_req_category:
      {
        if (tolerant)
          return 1;
        else
          return !has_branch && !has_version;
        break;
      }

    case arch_req_package:
      {
        if (tolerant)
          return 1;
        else
          return !has_version;
        break;
      }

    case arch_req_version:
      {
        if (tolerant)
          return has_version;
        else
          return has_version && !has_patch_level;
        break;
      }

    case arch_req_patch_level:
      {
        return has_patch_level;
      }

    default:
      {
        panic ("arch_valid_package_name: bad argument.");
        return 0;
      }
    }
}

/*(c arch_parse_package_name)
 * t_uchar * arch_parse_package_name (enum arch_parse_package_name_type type,
 *                                    t_uchar * default_archive,
 *                                    t_uchar * name);
 * 
 * Parse a package name.
 * 
 * `type' may be any of the values:
 * 
 * 	arch_ret_archive	Return the archive component of the name,
 * 				or the value `default_archive' if the name
 * 				is not fully qualified.
 * 
 * 	arch_ret_non_archive	Return all of the name except its (optional)
 * 				archive component.
 * 
 * 	arch_ret_category	Return just the category name.
 * 
 * 	arch_ret_package	Return the category--branch if the name has a 
 * 				branch label, just category otherwise.
 * 
 * 	arch_ret_version	Return just the version id of the name.
 * 
 * 	arch_ret_package_version  Return the category(--branch)?--version
 * 				of the name
 * 
 *	arch_ret_patch_level	Return just the revision id of the name
 *				
 * 
 * Note that there is no `arch_ret_branch' (since not all names have branch labels)
 * and no `arch_ret_package_patch_level' (since `arch_ret_non_archive' will do that
 * job).
 * 
 * `default_archive' is the archive name to use for `arch_ret_archive' if the
 * name is not fully qualified.   Typically, the value of `default_archive'
 * is taken from a command-line -A argument or the user's .arch-params/=default-archive
 * file.
 * 
 * `name' is the name to parse.
 * 
 */
t_uchar * 
arch_parse_package_name (enum arch_parse_package_name_type type,
                         t_uchar * default_archive,
                         t_uchar * name)
{
  invariant (arch_valid_package_name (name, arch_maybe_archive, arch_req_package, 1));

  switch (type)
    {
    default:
      panic ("bad argument to arch_parse_package_name");
      return 0;                 /* notreached */
      break;

    case arch_ret_archive:
      {
        t_uchar * slash;

        slash = str_chr_index (name, '/');
        if (!slash)
          {
            return arch_my_default_archive (default_archive);
          }
        else
          {
            return str_save_n (0, name, slash - name);
          }
        break;
      }

    case arch_ret_non_archive:
      {
        t_uchar * slash;

        slash = str_chr_index (name, '/');
        if (!slash)
          return str_save (0, name);
        else
          return str_save (0, slash + 1);

        break;
      }

    case arch_ret_category:
      {
        t_uchar * cat_end;

        name = over_opt_archive_prefix (name);
        cat_end = over_basename (name);
        return str_save_n (0, name, cat_end - name);
        break;
      }

    case arch_ret_branch:
      {
        t_uchar * branch_end;

        name = over_opt_archive_prefix (name);
        name = over_basename (name); /* over category */
        name = over_separator (name);
        if (!name)
          return str_save (0, "");
        branch_end = over_basename (name);
        if (!branch_end)
          return str_save (0, "");
        return str_save_n (0, name, branch_end - name);
        break;
      }

    case arch_ret_package:
      {
        t_uchar * branch_end;
        t_uchar * t;

        name = over_opt_archive_prefix (name);
        branch_end = over_basename (name);
        t = over_separator (branch_end);
        if (!t)
          return str_save (0, name); /* only category provided */
        branch_end = over_basename (t); /* over category */
        if (!branch_end)
          return str_save_n (0, name, (t - 2) - name); /* category--version */
        else
          return str_save_n (0, name, branch_end - name);
        break;
      }

    case arch_ret_version:
      {
        t_uchar * version_start;
        t_uchar * version_end;
        t_uchar * t;

        name = over_opt_archive_prefix (name);
        version_end = over_basename (name); /* over category */
        version_end = over_separator (version_end);
        invariant (!!version_end);
        version_start = version_end;
        t = over_basename (version_start); /* maybe over explicit branch */
        if (t)
          {
            version_end = over_separator (t);
            invariant (!!version_end);
            version_start = version_end;
          }
        version_end = over_version (version_end);
        invariant (!!version_end);
        return str_save_n (0, version_start, version_end - version_start);
        break;
      }

    case arch_ret_patch_level:
      {
        t_uchar * t;

        name = over_opt_archive_prefix (name);
        invariant (!!name);
        name = over_basename (name);
        invariant (!!name);
        name = over_separator (name);
        invariant (!!name);
        t = over_basename (name); /* maybe over explicit branch */
        if (t)
          {
            name = over_separator (t);
            invariant (!!name);
          }
        name = over_version (name);
        invariant (!!name);
        invariant (name[0]);
        name = over_separator (name);
        invariant (!!name);
        invariant (name[0]);
        return str_save (0, name);
        break;
      }

    case arch_ret_package_version:
      {
        t_uchar * version_start;
        t_uchar * version_end;
        t_uchar * t;

        name = over_opt_archive_prefix (name);
        version_end = over_basename (name); /* over category */
        version_end = over_separator (version_end);
        invariant (!!version_end);
        version_start = version_end;
        t = over_basename (version_start); /* maybe over explicit branch */
        if (t)
          {
            version_end = over_separator (t);
            invariant (!!version_end);
            version_start = version_end;
          }
        version_end = over_version (version_end);
        invariant (!!version_end);
        return str_save_n (0, name, version_end - name);
        break;
      }
    }
}

t_uchar *
arch_fully_qualify (t_uchar * default_archive, t_uchar * name)
{
  invariant (arch_valid_package_name (name, arch_maybe_archive, arch_req_package, 1));
  if (default_archive)
    invariant (arch_valid_archive_name (default_archive));

  if (str_chr_index (name, '/'))
    return str_save (0, name);
  else
    {
      if (!default_archive)
        default_archive = arch_my_default_archive (0);

      if (!default_archive)
        panic ("no default archive set");

      return str_alloc_cat_many (0, default_archive, "/", name, str_end);
    }
}

int
arch_names_cmp (t_uchar * a, t_uchar * b)
{
  t_uchar * a_nxt;
  t_uchar * b_nxt;
  int res;

  /* compare archive name
   */
  a_nxt = over_opt_archive_prefix (a);
  b_nxt = over_opt_archive_prefix (b);

  res = str_cmp_n (a, a_nxt - a, b, b_nxt - b);

  if (res)
    return res;

  a = a_nxt;
  b = b_nxt;

  /* compare category name
   */

  a_nxt = over_basename (a);
  b_nxt = over_basename (b);

  res = str_cmp_n (a, a_nxt - a, b, b_nxt - b);

  if (res)
    return res;

  if (!*a_nxt && !*b_nxt)
    return 0;

  if (!*a_nxt)
    return -1;

  if (!*b_nxt)
    return 1;

  a = over_separator (a_nxt);
  b = over_separator (b_nxt);


  /* compare branch names
   */

  a_nxt = over_basename (a);
  b_nxt = over_basename (b);

  if (a_nxt && !b_nxt)
    {
      /* a has a branch, b does not */
      return 1;
    }
  else if (!a_nxt && b_nxt)
    {
      /* b has a branch, a does not */
      return -1;
    }
  else if (a_nxt && b_nxt)
    {
      /* both have branch names */

      res = str_cmp_n (a, a_nxt - a, b, b_nxt - b);

      if (res)
        return res;

      if (!*a_nxt && !*b_nxt)
        return 0;

      if (!*a_nxt)
        return -1;

      if (!*b_nxt)
        return 1;

      a = over_separator (a_nxt);
      b = over_separator (b_nxt);
    }

  /* compare version names
   */
  
  a_nxt = over_version (a);
  b_nxt = over_version (b);

  {
    int a_vsn_len;
    int b_vsn_len;

    a_vsn_len = a_nxt - a;
    b_vsn_len = b_nxt - b;

    /* precondition:
     *    a and b point to a version number component
     *    a_vsn_len and b_vsn_len tell how many chars remain in the version id
     *
     * postcondition:
     *    RETURNS if version ids differ
     *    a and b point past version id otherwise
     *
     * goal:
     *    compare dot-separated components numerically
     *    noting that a and b may have different numbers of components
     */
    while (1)
      {
        int errn;
        t_uchar * a_comp_end;
        t_uchar * b_comp_end;
        t_ulong a_comp;
        t_ulong b_comp;

        a_comp_end = str_chr_index_n (a, a_vsn_len, '.');
        if (!a_comp_end)
          a_comp_end = a + a_vsn_len;

        b_comp_end = str_chr_index_n (b, b_vsn_len, '.');
        if (!b_comp_end)
          b_comp_end = b + b_vsn_len;

        invariant (!cvt_decimal_to_ulong (&errn, &a_comp, a, a_comp_end - a));
        invariant (!cvt_decimal_to_ulong (&errn, &b_comp, b, b_comp_end - b));

        if (a_comp < b_comp)
          return -1;
        else if (a_comp > b_comp)
          return 1;
        else
          {
            a_vsn_len -= (a_comp_end - a);
            b_vsn_len -= (b_comp_end - b);

            if (a_vsn_len && b_vsn_len)
              {
                /* both have additional components
                 */
                a = a_comp_end + 1;
                b = b_comp_end + 1;
                --a_vsn_len;
                --b_vsn_len;
              }
            else if (a_vsn_len && !b_vsn_len)
              {
                /* b has more components, a doesn't.
                 */
                return -1;
              }
            else if (!a_vsn_len && b_vsn_len)
              {
                /* a has more components, b doesn't.
                 */
                return 1;
              }
            else
              {
                /* neither has more components
                 */
                a = a_comp_end;
                b = b_comp_end;
                break;
              }
          }
      }

    a_nxt = over_separator (a);
    b_nxt = over_separator (b);

    if (!a_nxt && !b_nxt)
      return 0;
    else if (a_nxt && !b_nxt)
      return 1;
    else if (!a_nxt && b_nxt)
      return -1;
    else
      {
        a = a_nxt;
        b = b_nxt;
      }
  }

  

  /* compare patch level phase
   */
  {
    t_uchar * a_dash;
    t_uchar * b_dash;

    a_dash = str_chr_index (a, '-');
    b_dash = str_chr_index (b, '-');

    invariant (a && b);

    res = str_cmp_n (a, a_dash - a, b, b_dash - b);
    if (res)
      return res;

    a = a_dash + 1;
    b = b_dash + 1;
  }

  /* compare patch level numbers
   */
  {
    int errn;
    t_ulong a_lvl;
    t_ulong b_lvl;

    invariant (!cvt_decimal_to_ulong (&errn, &a_lvl, a, str_length (a)));
    invariant (!cvt_decimal_to_ulong (&errn, &b_lvl, b, str_length (b)));

    if (a_lvl < b_lvl)
      return -1;
    else if (a_lvl > b_lvl)
      return 1;
    else
      return 0;
  }
}

struct arch_sort_spec
{
  int reverse_p;
  int field;
};

void
arch_sort_table_by_name_field (int reverse_p, rel_table table, int field)
{
  struct arch_sort_spec spec;

  spec.reverse_p = reverse_p;
  spec.field = field;

  quicksort ((void *)table, rel_n_records (table), sizeof (rel_record), arch_cmp_by_field, (void *)&spec);
}

static int
arch_cmp_by_field (void * va, void * vb, void * vdata)
{
  rel_record * a;
  rel_record * b;
  struct arch_sort_spec * spec;

  a = (rel_record *)va;
  b = (rel_record *)vb;
  spec = (struct arch_sort_spec *)vdata;

  if (spec->reverse_p)
    {
      return -arch_names_cmp ((*a)[spec->field], (*b)[spec->field]);
    }
  else
    {
      return arch_names_cmp ((*a)[spec->field], (*b)[spec->field]);
    }
}

int 
arch_patch_lvl_cmp (t_uchar * a, t_uchar * b)
{
  t_uchar * a_dash;
  t_uchar * b_dash;
  int res;
  t_ulong a_lvl;
  t_ulong b_lvl;
  int errn;

  a_dash = str_chr_index (a, '-');
  b_dash = str_chr_index (b, '-');
  invariant (a_dash && b_dash);

  res = str_cmp_n (a, a_dash - a, b, b_dash - b);
  if (res)
    return res;

  invariant (!cvt_decimal_to_ulong (&errn, &a_lvl, a_dash + 1, str_length (a_dash + 1)));
  invariant (!cvt_decimal_to_ulong (&errn, &b_lvl, b_dash + 1, str_length (b_dash + 1)));

  if (a_lvl < b_lvl)
    return -1;
  else if (a_lvl > b_lvl)
    return 1;
  else
    return 0;
}

void
arch_sort_table_by_patch_level_field (int reverse_p, rel_table table, int field)
{
  struct arch_sort_spec spec;

  spec.reverse_p = reverse_p;
  spec.field = field;

  quicksort ((void *)table, rel_n_records (table), sizeof (rel_record), arch_lvl_cmp_by_field, (void *)&spec);
}

static int
arch_lvl_cmp_by_field (void * va, void * vb, void * vdata)
{
  rel_record * a;
  rel_record * b;
  struct arch_sort_spec * spec;

  a = (rel_record *)va;
  b = (rel_record *)vb;
  spec = (struct arch_sort_spec *)vdata;

  if (spec->reverse_p)
    {
      return -arch_patch_lvl_cmp ((*a)[spec->field], (*b)[spec->field]);
    }
  else
    {
      return arch_patch_lvl_cmp ((*a)[spec->field], (*b)[spec->field]);
    }
}


rel_table
arch_pick_archives_by_field (rel_table in, int field)
{
  rel_table answer = 0;
  int x;

  for (x = 0; x < rel_n_records (in); ++x)
    {
      if (arch_valid_archive_name (in[x][field]))
        rel_add_records (&answer, rel_copy_record (in[x]), 0);
    }

  return answer;
}


rel_table
arch_pick_categories_by_field (rel_table in, int field)
{
  rel_table answer = 0;
  int x;

  for (x = 0; x < rel_n_records (in); ++x)
    {
      if (arch_valid_package_name (in[x][field], arch_no_archive, arch_req_category, 0))
        rel_add_records (&answer, rel_copy_record (in[x]), 0);
    }

  return answer;
}

rel_table
arch_pick_branches_by_field (rel_table in, int field)
{
  rel_table answer = 0;
  int x;

  for (x = 0; x < rel_n_records (in); ++x)
    {
      if (arch_valid_package_name (in[x][field], arch_no_archive, arch_req_package, 0))
        rel_add_records (&answer, rel_copy_record (in[x]), 0);
    }

  return answer;
}

rel_table
arch_pick_versions_by_field (rel_table in, int field)
{
  rel_table answer = 0;
  int x;

  for (x = 0; x < rel_n_records (in); ++x)
    {
      if (arch_valid_package_name (in[x][field], arch_no_archive, arch_req_version, 0))
        rel_add_records (&answer, rel_copy_record (in[x]), 0);
    }

  return answer;
}

rel_table
arch_pick_revisions_by_field (rel_table in, int field)
{
  rel_table answer = 0;
  int x;

  for (x = 0; x < rel_n_records (in); ++x)
    {
      if (arch_valid_package_name (in[x][field], arch_no_archive, arch_req_patch_level, 0))
        rel_add_records (&answer, rel_copy_record (in[x]), 0);
    }

  return answer;
}

rel_table
arch_pick_patch_levels_by_field (rel_table in, int field)
{
  rel_table answer = 0;
  int x;

  for (x = 0; x < rel_n_records (in); ++x)
    {
      if (arch_valid_patch_level_name (in[x][field]))
        rel_add_records (&answer, rel_copy_record (in[x]), 0);
    }

  return answer;
}


enum arch_patch_level_type
arch_analyze_patch_level (t_ulong * n, t_uchar * patch_level)
{
  int ign;
  enum arch_patch_level_type type;
  t_uchar * n_str;

  if (!str_cmp ("base-0", patch_level))
    {
      *n = 0;
      return arch_is_base0_level;
    }
  else if (!str_cmp_prefix ("patch-", patch_level))
    {
      type = arch_is_patch_level;
      n_str = patch_level + sizeof ("patch-") - 1;
    }
  else if (!str_cmp_prefix ("version-", patch_level))
    {
      type = arch_is_version_level;
      n_str = patch_level + sizeof ("version-") - 1;
    }
  else if (!str_cmp_prefix ("versionfix-", patch_level))
    {
      type = arch_is_versionfix_level;
      n_str = patch_level + sizeof ("versionfix-") - 1;
    }
  else
    panic ("illegal patch_level in arch_analyze_patch_level");

 if (cvt_decimal_to_ulong (&ign, n, n_str, str_length (n_str)))
   panic ("illegal patch_level in arch_analyze_patch_level");

  return type;
}


t_uchar *
arch_form_patch_level (enum arch_patch_level_type type, t_ulong n)
{
  t_uchar n_buf[64];

  cvt_ulong_to_decimal (n_buf, n);

  switch (type)
    {
    case arch_is_base0_level:
      {
        return str_save (0, "base-0");
      }
    case arch_is_patch_level:
      {
        return str_alloc_cat (0, "patch-", n_buf);
      }
    case arch_is_version_level:
      {
        return str_alloc_cat (0, "version-", n_buf);
      }
    case arch_is_versionfix_level:
      {
        return str_alloc_cat (0, "versionfix-", n_buf);
      }
    default:
      {
        panic ("not reached in arch_form_patch_level");
        return 0;
      }
    }
}



static t_uchar * 
over_opt_archive_prefix (t_uchar * name)
{
  t_uchar * slash;
  t_uchar * archive_name_end;

  slash = str_chr_index (name, '/');

  if (!slash)
    return name;

  archive_name_end = over_archive_name (name);

  if (archive_name_end != slash)
    return 0;

  return slash + 1;
}


#define char_is_alnum_or_dash(c) (char_is_alnum (c) || (c == '-'))

static t_uchar * 
over_archive_name (t_uchar * in)
{
  if (!char_is_alnum_or_dash (*in))
    return 0;

  while (1)
    {
      while (char_is_alnum_or_dash (*in))
        ++in;

      if (*in != '.')
        break;

      if (!char_is_alnum_or_dash (in[1]))
        return 0;

      ++in;
    }

  if (*in != '@')
    return 0;

  ++in;

  while (char_is_alnum_or_dash(*in) || (*in == '.'))
    {
      if ((*in == '-') && (in[1] == '-'))
        break;
      ++in;
    }

  if (!*in || (*in == '/'))
    return in;

  if ((in[0] != '-') || (in[1] != '-'))
    return 0;

  in += 2;
  
  while (char_is_alnum_or_dash(*in) || (*in == '.'))
    ++in;

  if (*in && (*in != '/'))
    return 0;

  return in;
}


static t_uchar *
over_basename (t_uchar * in)
{
  if (!char_is_alpha (*in))
    return 0;
  ++in;

  while (1)
    {
      if (!*in)
        return in;
      else if (char_is_alnum (*in))
        ++in;
      else if (*in == '-')
        {
          if (in[1] == '-')
            return in;
          else
            ++in;
        }
      else
        break;
    }
  return 0;
}

static t_uchar *
over_separator (t_uchar * in)
{
  if ((*in == '-') && (in[1] == '-'))
    return in + 2;
  else
    return 0;
}

static t_uchar *
over_version (t_uchar * in)
{
  int components;

  for (components = 0; components < 2; ++components)
    {
      if (!*in)
        break;
      else if (!char_is_digit (*in))
        break;
      else
        {
          while (char_is_digit (*in))
            ++in;
          if (!*in)
            return in;
          else if (*in == '.')
            ++in;
          else if ((*in == '-') && (in[1] == '-'))
            return in;
          else
            break;
        }
    }
  return 0;
}

static t_uchar *
over_patch_level (t_uchar * in)
{
  if (!str_cmp (in, "base-0"))
    return in + sizeof ("base-0") - 1;
  else
    {
      int prefix_len;

      if (!str_cmp_prefix ("patch-", in))
        prefix_len = sizeof ("patch-") - 1;
      else if (!str_cmp_prefix ("version-", in))
        prefix_len = sizeof ("version-") - 1;
      else if (!str_cmp_prefix ("versionfix-", in))
        prefix_len = sizeof ("versionfix-") - 1;
      else
        return 0;

      in += prefix_len;

      if (!char_is_digit (*in))
        return 0;

      while (char_is_digit (*in))
        ++in;

      if (*in)
        return 0;

      return in;
    }
}




/* tag: Tom Lord Mon May 12 10:17:47 2003 (namespace.c)
 */
