Blame src/conda.c

Packit Service ff689b
/*
Packit Service ff689b
 * Copyright (c) 2019, SUSE LLC
Packit Service ff689b
 *
Packit Service ff689b
 * This program is licensed under the BSD license, read LICENSE.BSD
Packit Service ff689b
 * for further information
Packit Service ff689b
 */
Packit Service ff689b
Packit Service ff689b
/*
Packit Service ff689b
 * conda.c
Packit Service ff689b
 *
Packit Service ff689b
 * evr comparison and package matching for conda
Packit Service ff689b
 */
Packit Service ff689b
Packit Service ff689b
#include <stdio.h>
Packit Service ff689b
#include <stdlib.h>
Packit Service ff689b
#include <stdarg.h>
Packit Service ff689b
#include <unistd.h>
Packit Service ff689b
#include <string.h>
Packit Service ff689b
#include <sys/types.h>
Packit Service ff689b
#include <regex.h>
Packit Service ff689b
Packit Service ff689b
#include "pool.h"
Packit Service ff689b
#include "repo.h"
Packit Service ff689b
#include "util.h"
Packit Service ff689b
#include "conda.h"
Packit Service ff689b
Packit Service ff689b
#ifdef _WIN32
Packit Service ff689b
#include "strfncs.h"
Packit Service ff689b
#endif
Packit Service ff689b
Packit Service ff689b
static const char *
Packit Service ff689b
endseg(const char *seg, const char *end)
Packit Service ff689b
{
Packit Service ff689b
  for (; seg < end; seg++)
Packit Service ff689b
    if (*seg == '.' || *seg == '-' || *seg == '_')
Packit Service ff689b
      break;
Packit Service ff689b
  return seg;
Packit Service ff689b
}
Packit Service ff689b
Packit Service ff689b
static const char *
Packit Service ff689b
endpart(const char *seg, const char *end)
Packit Service ff689b
{
Packit Service ff689b
  if (seg == end)
Packit Service ff689b
    return seg;
Packit Service ff689b
  if (*seg >= '0' && *seg <= '9')
Packit Service ff689b
    {
Packit Service ff689b
      for (seg++; seg < end; seg++)
Packit Service ff689b
        if (!(*seg >= '0' && *seg <= '9'))
Packit Service ff689b
	  break;
Packit Service ff689b
    }
Packit Service ff689b
  else if (*seg == '*')
Packit Service ff689b
    {
Packit Service ff689b
      for (seg++; seg < end; seg++)
Packit Service ff689b
	if (*seg != '*')
Packit Service ff689b
	  break;
Packit Service ff689b
    }
Packit Service ff689b
  else
Packit Service ff689b
    {
Packit Service ff689b
      for (seg++; seg < end; seg++)
Packit Service ff689b
        if ((*seg >= '0' && *seg <= '9') || *seg == '*')
Packit Service ff689b
	  break;
Packit Service ff689b
    }
Packit Service ff689b
  return seg;
Packit Service ff689b
}
Packit Service ff689b
Packit Service ff689b
/* C implementation of the version comparison code in conda/models/version.py */
Packit Service ff689b
/* startswith == 1 : check if s1 starts with s2 */
Packit Service ff689b
static int
Packit Service ff689b
solv_vercmp_conda(const char *s1, const char *q1, const char *s2, const char *q2, int startswith)
Packit Service ff689b
{
Packit Service ff689b
  const char *s1p, *s2p;
Packit Service ff689b
  const char *s1e, *s2e;
Packit Service ff689b
  int r, isfirst;
Packit Service ff689b
  const char *q2end = 0;
Packit Service ff689b
Packit Service ff689b
  if (startswith)
Packit Service ff689b
    {
Packit Service ff689b
      for (q2end = q2; q2end > s2; q2end--)
Packit Service ff689b
	if (q2end[-1] != '.' && q2end[-1] != '-' && q2end[-1] != '_')
Packit Service ff689b
	  break;
Packit Service ff689b
    }
Packit Service ff689b
  for (;;)
Packit Service ff689b
    {
Packit Service ff689b
      while (s1 < q1 && (*s1 == '.' || *s1 == '-' || *s1 == '_'))
Packit Service ff689b
	s1++;
Packit Service ff689b
      while (s2 < q2 && (*s2 == '.' || *s2 == '-' || *s2 == '_'))
Packit Service ff689b
	s2++;
Packit Service ff689b
      if (s1 == q1 && s2 == q2)
Packit Service ff689b
	return 0;
Packit Service ff689b
      if (startswith && s2 == q2)
Packit Service ff689b
	return 0;
Packit Service ff689b
      /* find end of component */
Packit Service ff689b
      s1e = endseg(s1, q1);
Packit Service ff689b
      s2e = endseg(s2, q2);
Packit Service ff689b
      
Packit Service ff689b
      for (isfirst = 1; ; isfirst = 0)
Packit Service ff689b
	{
Packit Service ff689b
	  if (s1 == s1e && s2 == s2e)
Packit Service ff689b
	    break;
Packit Service ff689b
	  if (s2 == q2end)
Packit Service ff689b
	    return 0;
Packit Service ff689b
          s1p = endpart(s1, s1e);
Packit Service ff689b
          s2p = endpart(s2, s2e);
Packit Service ff689b
	  /* prepend 0 if not numeric */
Packit Service ff689b
	  if (isfirst)
Packit Service ff689b
	    {
Packit Service ff689b
	      if (s1p != s1 && !(*s1 >= '0' && *s1 <= '9'))
Packit Service ff689b
		s1p = s1;
Packit Service ff689b
	      if (s2p != s2 && !(*s2 >= '0' && *s2 <= '9'))
Packit Service ff689b
		s2p = s2;
Packit Service ff689b
	    }
Packit Service ff689b
	  /* special case "post" */
Packit Service ff689b
	  if (s1p - s1 == 4 && !strncasecmp(s1, "post", 4))
Packit Service ff689b
	    {
Packit Service ff689b
	      if (s2p - s2 == 4 && !strncasecmp(s2, "post", 4))
Packit Service ff689b
		{
Packit Service ff689b
		  s1 = s1p;
Packit Service ff689b
		  s2 = s2p;
Packit Service ff689b
		  continue;
Packit Service ff689b
		}
Packit Service ff689b
	      return 1;
Packit Service ff689b
	    }
Packit Service ff689b
	  if (s2p - s2 == 4 && !strncasecmp(s2, "post", 4))
Packit Service ff689b
	    return -1;
Packit Service ff689b
Packit Service ff689b
	  if (isfirst || ((s1 == s1p || (*s1 >= '0' && *s1 <= '9')) && (s2 == s2p || (*s2 >= '0' && *s2 <= '9'))))
Packit Service ff689b
	    {
Packit Service ff689b
	      /* compare numbers */
Packit Service ff689b
	      while (s1 < s1p && *s1 == '0')
Packit Service ff689b
		s1++;
Packit Service ff689b
	      while (s2 < s2p && *s2 == '0')
Packit Service ff689b
		s2++;
Packit Service ff689b
	      if (s1p - s1 < s2p - s2)
Packit Service ff689b
		return -1;
Packit Service ff689b
	      if (s1p - s1 > s2p - s2)
Packit Service ff689b
		return 1;
Packit Service ff689b
	      r = s1p - s1 ? strncmp(s1, s2, s1p - s1) : 0;
Packit Service ff689b
	      if (r)
Packit Service ff689b
		return r;
Packit Service ff689b
	    }
Packit Service ff689b
	  else if (s1 == s1p || (*s1 >= '0' && *s1 <= '9'))
Packit Service ff689b
	    return 1;
Packit Service ff689b
	  else if (s2 == s2p || (*s2 >= '0' && *s2 <= '9'))
Packit Service ff689b
	    return -1;
Packit Service ff689b
	  else
Packit Service ff689b
	    {
Packit Service ff689b
	      /* special case "dev" */
Packit Service ff689b
	      if (*s2 != '*' && s1p - s1 == 3 && !strncasecmp(s1, "dev", 3))
Packit Service ff689b
		{
Packit Service ff689b
		  if (s2p - s2 == 3 && !strncasecmp(s2, "dev", 3))
Packit Service ff689b
		    {
Packit Service ff689b
		      s1 = s1p;
Packit Service ff689b
		      s2 = s2p;
Packit Service ff689b
		      continue;
Packit Service ff689b
		    }
Packit Service ff689b
		  return -1;
Packit Service ff689b
		}
Packit Service ff689b
	      if (*s1 != '*' && s2p - s2 == 3 && !strncasecmp(s2, "dev", 3))
Packit Service ff689b
		return 1;
Packit Service ff689b
	      /* compare strings */
Packit Service ff689b
	      r = s2p - s2 > s1p - s1 ? s1p - s1 : s2p - s2;
Packit Service ff689b
	      if (r)
Packit Service ff689b
	        r = strncasecmp(s1, s2, r);
Packit Service ff689b
	      if (r)
Packit Service ff689b
		return r;
Packit Service ff689b
              if (s1p - s1 < s2p - s2) 
Packit Service ff689b
                return -1; 
Packit Service ff689b
              if (s1p - s1 > s2p - s2) 
Packit Service ff689b
                return 1;
Packit Service ff689b
	    }
Packit Service ff689b
	  s1 = s1p;
Packit Service ff689b
	  s2 = s2p;
Packit Service ff689b
	}
Packit Service ff689b
    }
Packit Service ff689b
}
Packit Service ff689b
Packit Service ff689b
static int
Packit Service ff689b
pool_evrcmp_conda_int(const char *evr1, const char *evr1e, const char *evr2, const char *evr2e, int startswith)
Packit Service ff689b
{
Packit Service ff689b
  static char zero[2] = {'0', 0};
Packit Service ff689b
  const char *s1, *s2;
Packit Service ff689b
  const char *r1, *r2;
Packit Service ff689b
  int r;
Packit Service ff689b
Packit Service ff689b
  /* split and compare epoch */
Packit Service ff689b
  for (s1 = evr1; s1 < evr1e && *s1 >= '0' && *s1 <= '9'; s1++)
Packit Service ff689b
    ;
Packit Service ff689b
  for (s2 = evr2; s2 < evr2e && *s2 >= '0' && *s2 <= '9'; s2++)
Packit Service ff689b
    ;
Packit Service ff689b
  if (s1 == evr1 || s1 == evr1e || *s1 != '!')
Packit Service ff689b
    s1 = 0;
Packit Service ff689b
  if (s2 == evr1 || s2 == evr2e || *s2 != '!')
Packit Service ff689b
    s2 = 0;
Packit Service ff689b
  if (s1 || s2)
Packit Service ff689b
    {
Packit Service ff689b
      r = solv_vercmp_conda(s1 ? evr1 : zero, s1 ? s1 : zero + 1, 
Packit Service ff689b
                            s2 ? evr2 : zero, s2 ? s2 : zero + 1, 0);
Packit Service ff689b
      if (r)
Packit Service ff689b
	return r;
Packit Service ff689b
      if (s1)
Packit Service ff689b
        evr1 = s1 + 1;
Packit Service ff689b
      if (s2)
Packit Service ff689b
        evr2 = s2 + 1;
Packit Service ff689b
    }
Packit Service ff689b
  /* split into version/localversion */
Packit Service ff689b
  for (s1 = evr1, r1 = 0; s1 < evr1e; s1++)
Packit Service ff689b
    if (*s1 == '+')
Packit Service ff689b
      r1 = s1;
Packit Service ff689b
  for (s2 = evr2, r2 = 0; s2 < evr2e; s2++)
Packit Service ff689b
    if (*s2 == '+')
Packit Service ff689b
      r2 = s2;
Packit Service ff689b
  r = solv_vercmp_conda(evr1, r1 ? r1 : s1, evr2, r2 ? r2 : s2, r2 ? 0 : startswith);
Packit Service ff689b
  if (r)
Packit Service ff689b
    return r;
Packit Service ff689b
  if (!r1 && !r2)
Packit Service ff689b
    return 0;
Packit Service ff689b
  if (!r1 && r2)
Packit Service ff689b
    return -1;
Packit Service ff689b
  if (r1 && !r2)
Packit Service ff689b
    return 1;
Packit Service ff689b
  return solv_vercmp_conda(r1 + 1, s1, r2 + 1, s2, startswith);
Packit Service ff689b
}
Packit Service ff689b
Packit Service ff689b
int
Packit Service ff689b
pool_evrcmp_conda(const Pool *pool, const char *evr1, const char *evr2, int mode)
Packit Service ff689b
{
Packit Service ff689b
  if (evr1 == evr2)
Packit Service ff689b
    return 0;
Packit Service ff689b
  return pool_evrcmp_conda_int(evr1, evr1 + strlen(evr1), evr2, evr2 + strlen(evr2), 0);
Packit Service ff689b
}
Packit Service ff689b
Packit Service ff689b
static int
Packit Service ff689b
regexmatch(const char *evr, const char *version, size_t versionlen, int icase)
Packit Service ff689b
{
Packit Service ff689b
  regex_t reg;
Packit Service ff689b
  char *buf = solv_malloc(versionlen + 1);
Packit Service ff689b
  int r;
Packit Service ff689b
Packit Service ff689b
  memcpy(buf, version, versionlen);
Packit Service ff689b
  buf[versionlen] = 0;
Packit Service ff689b
  if (regcomp(&reg, buf, REG_EXTENDED | REG_NOSUB | (icase ? REG_ICASE : 0)))
Packit Service ff689b
    {
Packit Service ff689b
      solv_free(buf);
Packit Service ff689b
      return 0;
Packit Service ff689b
    }
Packit Service ff689b
  r = regexec(&reg, evr, 0, NULL, 0);
Packit Service ff689b
  regfree(®);
Packit Service ff689b
  solv_free(buf);
Packit Service ff689b
  return r == 0;
Packit Service ff689b
}
Packit Service ff689b
Packit Service ff689b
static int
Packit Service ff689b
globmatch(const char *evr, const char *version, size_t versionlen, int icase)
Packit Service ff689b
{
Packit Service ff689b
  regex_t reg;
Packit Service ff689b
  char *buf = solv_malloc(2 * versionlen + 3);
Packit Service ff689b
  size_t i, j;
Packit Service ff689b
  int r;
Packit Service ff689b
Packit Service ff689b
  buf[0] = '^';
Packit Service ff689b
  j = 1;
Packit Service ff689b
  for (i = 0, j = 1; i < versionlen; i++)
Packit Service ff689b
    {
Packit Service ff689b
      if (version[i] == '.' || version[i] == '+' || version[i] == '*')
Packit Service ff689b
	buf[j++] = version[i] == '*' ? '.' : '\\';
Packit Service ff689b
      buf[j++] = version[i];
Packit Service ff689b
    }
Packit Service ff689b
  buf[j++] = '$';
Packit Service ff689b
  buf[j] = 0;
Packit Service ff689b
  if (regcomp(&reg, buf, REG_EXTENDED | REG_NOSUB | (icase ? REG_ICASE : 0)))
Packit Service ff689b
    {
Packit Service ff689b
      solv_free(buf);
Packit Service ff689b
      return 0;
Packit Service ff689b
    }
Packit Service ff689b
  r = regexec(&reg, evr, 0, NULL, 0);
Packit Service ff689b
  regfree(®);
Packit Service ff689b
  solv_free(buf);
Packit Service ff689b
  return r == 0;
Packit Service ff689b
}
Packit Service ff689b
Packit Service ff689b
/* return true if solvable s matches the version */
Packit Service ff689b
/* see conda/models/version.py */
Packit Service ff689b
static int
Packit Service ff689b
solvable_conda_matchversion_single(Solvable *s, const char *version, size_t versionlen)
Packit Service ff689b
{
Packit Service ff689b
  const char *evr;
Packit Service ff689b
  size_t i;
Packit Service ff689b
  int r;
Packit Service ff689b
Packit Service ff689b
  if (versionlen == 0 || (versionlen == 1 && *version == '*'))
Packit Service ff689b
    return 1;	/* matches every version */
Packit Service ff689b
  evr = pool_id2str(s->repo->pool, s->evr);
Packit Service ff689b
  if (versionlen >= 2 && version[0] == '^' && version[versionlen - 1] == '$')
Packit Service ff689b
    return regexmatch(evr, version, versionlen, 0);
Packit Service ff689b
  if (version[0] == '=' || version[0] == '<' || version[0] == '>' || version[0] == '!' || version[0] == '~')
Packit Service ff689b
    {
Packit Service ff689b
      int flags = 0;
Packit Service ff689b
      int oplen;
Packit Service ff689b
      if (version[0] == '=')
Packit Service ff689b
	  flags = version[1] == '=' ? REL_EQ : 8;
Packit Service ff689b
      else if (version[0] == '!' || version[0] == '~')
Packit Service ff689b
	{
Packit Service ff689b
	  if (version[1] != '=')
Packit Service ff689b
	    return 0;
Packit Service ff689b
	  flags = version[0] == '!' ? REL_LT | REL_GT : 9;
Packit Service ff689b
	}
Packit Service ff689b
      else if (version[0] == '<' || version[0] == '>')
Packit Service ff689b
	{
Packit Service ff689b
	  flags = version[0] == '<' ? REL_LT : REL_GT;
Packit Service ff689b
	  if (version[1] == '=')
Packit Service ff689b
	    flags |= REL_EQ;
Packit Service ff689b
	}
Packit Service ff689b
      else
Packit Service ff689b
	return 0;
Packit Service ff689b
      oplen = flags == 8 || flags == REL_LT || flags == REL_GT ? 1 : 2;
Packit Service ff689b
      if (versionlen < oplen + 1)
Packit Service ff689b
	return 0;
Packit Service ff689b
      version += oplen;
Packit Service ff689b
      versionlen -= oplen;
Packit Service ff689b
      if (version[0] == '=' || version[0] == '<' || version[0] == '>' || version[0] == '!' || version[0] == '~')
Packit Service ff689b
	return 0;		/* bad chars after op */
Packit Service ff689b
      if (versionlen >= 2 && version[versionlen - 2] == '.' && version[versionlen - 1] == '*')
Packit Service ff689b
	{
Packit Service ff689b
	  if (flags == 8 || flags == (REL_GT | REL_EQ))
Packit Service ff689b
	    versionlen -= 2;
Packit Service ff689b
	  else if (flags == (REL_LT | REL_GT))
Packit Service ff689b
	    {
Packit Service ff689b
	      versionlen -= 2;
Packit Service ff689b
	      flags = 10;
Packit Service ff689b
	    }
Packit Service ff689b
	  else
Packit Service ff689b
	    return 0;
Packit Service ff689b
	}
Packit Service ff689b
      if (flags < 8)
Packit Service ff689b
	{
Packit Service ff689b
	  /* we now have an op and a version */
Packit Service ff689b
	  r = pool_evrcmp_conda_int(evr, evr + strlen(evr), version, version + versionlen, 0);
Packit Service ff689b
	  if (r < 0)
Packit Service ff689b
	    return (flags & REL_LT) ? 1 : 0;
Packit Service ff689b
	  if (r == 0)
Packit Service ff689b
	    return (flags & REL_EQ) ? 1 : 0;
Packit Service ff689b
	  if (r > 0)
Packit Service ff689b
	    return (flags & REL_GT) ? 1 : 0;
Packit Service ff689b
	  return 0;
Packit Service ff689b
	}
Packit Service ff689b
      if (flags == 8 || flags == 10)	/* startswith, not-startswith */
Packit Service ff689b
	{
Packit Service ff689b
	  r = pool_evrcmp_conda_int(evr, evr + strlen(evr), version, version + versionlen, 1);
Packit Service ff689b
	  return flags == 8 ? r == 0 : r != 0;
Packit Service ff689b
	}
Packit Service ff689b
      else if (flags == 9)		/* compatible release op */
Packit Service ff689b
	{
Packit Service ff689b
	  r = pool_evrcmp_conda_int(evr, evr + strlen(evr), version, version + versionlen, 0);
Packit Service ff689b
	  if (r < 0)
Packit Service ff689b
	    return 0;
Packit Service ff689b
	  /* split off last component */
Packit Service ff689b
	  while (versionlen > 0 && version[versionlen - 1] != '.')
Packit Service ff689b
	    versionlen--;
Packit Service ff689b
	  if (versionlen < 2)
Packit Service ff689b
	    return 0;
Packit Service ff689b
	  versionlen--;
Packit Service ff689b
	  r = pool_evrcmp_conda_int(evr, evr + strlen(evr), version, version + versionlen, 1);
Packit Service ff689b
	  return r == 0 ? 1 : 0;
Packit Service ff689b
	}
Packit Service ff689b
      return 0;
Packit Service ff689b
    }
Packit Service ff689b
   
Packit Service ff689b
  /* do we have a '*' in the version */
Packit Service ff689b
  for (i = 0; i < versionlen; i++)
Packit Service ff689b
    if (version[i] == '*')
Packit Service ff689b
      {
Packit Service ff689b
        for (i++; i < versionlen; i++)
Packit Service ff689b
          if (version[i] != '*')
Packit Service ff689b
	    break;
Packit Service ff689b
	if (i < versionlen)
Packit Service ff689b
	  return globmatch(evr, version, versionlen, 1);
Packit Service ff689b
      }
Packit Service ff689b
Packit Service ff689b
  if (versionlen > 1 && version[versionlen - 1] == '*')
Packit Service ff689b
    {
Packit Service ff689b
      /* startswith */
Packit Service ff689b
      while (versionlen > 0 && version[versionlen - 1] == '*')
Packit Service ff689b
	versionlen--;
Packit Service ff689b
      while (versionlen > 0 && version[versionlen - 1] == '.')
Packit Service ff689b
	versionlen--;
Packit Service ff689b
      r = pool_evrcmp_conda_int(evr, evr + strlen(evr), version, version + versionlen, 1);
Packit Service ff689b
      return r == 0 ? 1 : 0;
Packit Service ff689b
    }
Packit Service ff689b
  /* do we have a '@' in the version? */
Packit Service ff689b
  for (i = 0; i < versionlen; i++)
Packit Service ff689b
    if (version[i] == '@')
Packit Service ff689b
      return strncmp(evr, version, versionlen) == 0 && evr[versionlen] == 0;
Packit Service ff689b
  r = pool_evrcmp_conda_int(evr, evr + strlen(evr), version, version + versionlen, 0);
Packit Service ff689b
  return r == 0 ? 1 : 0;
Packit Service ff689b
}
Packit Service ff689b
Packit Service ff689b
static int
Packit Service ff689b
solvable_conda_matchversion_rec(Solvable *s, const char **versionp, const char *versionend)
Packit Service ff689b
{
Packit Service ff689b
  const char *version = *versionp;
Packit Service ff689b
  int v, vor = 0, vand = -1;	/* -1: doing OR, 0,1: doing AND */
Packit Service ff689b
Packit Service ff689b
  if (version == versionend)
Packit Service ff689b
    return -1;
Packit Service ff689b
  for (;;)
Packit Service ff689b
    {
Packit Service ff689b
      if (*version == '(')
Packit Service ff689b
	{
Packit Service ff689b
	  version++;
Packit Service ff689b
	  v = solvable_conda_matchversion_rec(s, &version, versionend);
Packit Service ff689b
	  if (v == -1 || version == versionend || *version != ')')
Packit Service ff689b
	    return -1;
Packit Service ff689b
	  version++;
Packit Service ff689b
	}
Packit Service ff689b
      else if (*version == ')' || *version == '|' || *version == ',')
Packit Service ff689b
	return -1;
Packit Service ff689b
      else
Packit Service ff689b
	{
Packit Service ff689b
	  const char *vstart = version;
Packit Service ff689b
	  while (version < versionend && *version != '(' && *version != ')' && *version != '|' && *version != ',')
Packit Service ff689b
	    version++;
Packit Service ff689b
	  if (vand >= 0 ? !vand : vor)
Packit Service ff689b
	    v = 0;		/* no need to call expensive matchversion if the result does not matter */
Packit Service ff689b
	  else
Packit Service ff689b
	    v = solvable_conda_matchversion_single(s, vstart, version - vstart) ? 1 : 0;
Packit Service ff689b
	}
Packit Service ff689b
      if (version == versionend || *version == ')')
Packit Service ff689b
	{
Packit Service ff689b
 	  *versionp = version;
Packit Service ff689b
	  return vor | (vand >= 0 ? (vand & v) : v);
Packit Service ff689b
	}
Packit Service ff689b
      if (*version == ',')
Packit Service ff689b
	vand = vand >= 0 ? (vand & v) : v;
Packit Service ff689b
      else if (*version == '|')
Packit Service ff689b
	{
Packit Service ff689b
	  vor |= vand >= 0 ? (vand & v) : v;
Packit Service ff689b
	  vand = -1;
Packit Service ff689b
	}
Packit Service ff689b
      else
Packit Service ff689b
	return -1;
Packit Service ff689b
      version++;
Packit Service ff689b
    }
Packit Service ff689b
}
Packit Service ff689b
Packit Service ff689b
static int
Packit Service ff689b
solvable_conda_matchbuild(Solvable *s, const char *build, const char *buildend)
Packit Service ff689b
{
Packit Service ff689b
  const char *bp;
Packit Service ff689b
  const char *flavor = solvable_lookup_str(s, SOLVABLE_BUILDFLAVOR);
Packit Service ff689b
Packit Service ff689b
  if (!flavor)
Packit Service ff689b
    flavor = "";
Packit Service ff689b
  if (build == buildend)
Packit Service ff689b
    return *flavor ? 0 : 1;
Packit Service ff689b
  if (build + 1 == buildend && *build == '*')
Packit Service ff689b
    return 1;
Packit Service ff689b
  if (*build == '^' && buildend[-1] == '$')
Packit Service ff689b
    return regexmatch(flavor, build, buildend - build, 0);
Packit Service ff689b
  for (bp = build; bp < buildend; bp++)
Packit Service ff689b
    if (*bp == '*')
Packit Service ff689b
      return globmatch(flavor, build, buildend - build, 0);
Packit Service ff689b
  return strncmp(flavor, build, buildend - build) == 0 && flavor[buildend - build] == 0 ? 1 : 0;
Packit Service ff689b
}
Packit Service ff689b
Packit Service ff689b
/* return true if solvable s matches the version */
Packit Service ff689b
/* see conda/models/match_spec.py */
Packit Service ff689b
int
Packit Service ff689b
solvable_conda_matchversion(Solvable *s, const char *version)
Packit Service ff689b
{
Packit Service ff689b
  const char *build, *versionend;
Packit Service ff689b
  int r;
Packit Service ff689b
  /* split off build */
Packit Service ff689b
  if ((build = strchr(version, ' ')) != 0)
Packit Service ff689b
    {
Packit Service ff689b
      versionend = build++;
Packit Service ff689b
      while (*build == ' ')
Packit Service ff689b
	build++;
Packit Service ff689b
    }
Packit Service ff689b
  else
Packit Service ff689b
    versionend = version + strlen(version);
Packit Service ff689b
  r = solvable_conda_matchversion_rec(s, &version, versionend);
Packit Service ff689b
  if (r != 1 || version != versionend)
Packit Service ff689b
    return 0;
Packit Service ff689b
  if (build && !solvable_conda_matchbuild(s, build, build + strlen(build)))
Packit Service ff689b
    return 0;
Packit Service ff689b
  return r;
Packit Service ff689b
}
Packit Service ff689b
Packit Service ff689b
static Id
Packit Service ff689b
pool_addrelproviders_conda_slow(Pool *pool, const char *namestr, Id evr, Queue *plist, int mode)
Packit Service ff689b
{
Packit Service ff689b
  size_t namestrlen = strlen(namestr);
Packit Service ff689b
  const char *evrstr = evr == 0 || evr == 1 ? 0 : pool_id2str(pool, evr);
Packit Service ff689b
  Id p;
Packit Service ff689b
Packit Service ff689b
  FOR_POOL_SOLVABLES(p)
Packit Service ff689b
    {
Packit Service ff689b
      Solvable *s = pool->solvables + p;
Packit Service ff689b
      if (!pool_installable(pool, s))
Packit Service ff689b
	continue;
Packit Service ff689b
      if (mode == 1 && !globmatch(pool_id2str(pool, s->name), namestr, namestrlen, 1))
Packit Service ff689b
	continue;
Packit Service ff689b
      if (mode == 2 && !regexmatch(pool_id2str(pool, s->name), namestr, namestrlen, 1))
Packit Service ff689b
	continue;
Packit Service ff689b
      if (!evrstr || solvable_conda_matchversion(s, evrstr))
Packit Service ff689b
	queue_push(plist, p);
Packit Service ff689b
    }
Packit Service ff689b
  return 0;
Packit Service ff689b
}
Packit Service ff689b
Packit Service ff689b
/* called from pool_addrelproviders, plist is an empty queue
Packit Service ff689b
 * it can either return an offset into the whatprovides array
Packit Service ff689b
 * or fill the plist queue and return zero */
Packit Service ff689b
Id
Packit Service ff689b
pool_addrelproviders_conda(Pool *pool, Id name, Id evr, Queue *plist)
Packit Service ff689b
{
Packit Service ff689b
  const char *namestr = pool_id2str(pool, name), *np;
Packit Service ff689b
  size_t l, nuc = 0;
Packit Service ff689b
  Id wp, p, *pp;
Packit Service ff689b
Packit Service ff689b
  /* is this a regex? */
Packit Service ff689b
  if (*namestr && *namestr == '^')
Packit Service ff689b
    {
Packit Service ff689b
      l = strlen(namestr);
Packit Service ff689b
      if (namestr[l - 1] == '$')
Packit Service ff689b
	return pool_addrelproviders_conda_slow(pool, namestr, evr, plist, 2);
Packit Service ff689b
    }
Packit Service ff689b
  /* is this '*'? */
Packit Service ff689b
  if (*namestr && *namestr == '*' && namestr[1] == 0)
Packit Service ff689b
    return pool_addrelproviders_conda_slow(pool, namestr, evr, plist, 0);
Packit Service ff689b
  /* does the string contain '*' or uppercase? */
Packit Service ff689b
  for (np = namestr; *np; np++)
Packit Service ff689b
    {
Packit Service ff689b
      if (*np == '*')
Packit Service ff689b
	return pool_addrelproviders_conda_slow(pool, namestr, evr, plist, 1);
Packit Service ff689b
      else if (*np >= 'A' && *np <= 'Z')
Packit Service ff689b
        nuc++;
Packit Service ff689b
    }
Packit Service ff689b
  if (nuc)
Packit Service ff689b
    {
Packit Service ff689b
      char *nbuf = solv_strdup(namestr), *nbufp;
Packit Service ff689b
      for (nbufp = nbuf; *nbufp; nbufp++)
Packit Service ff689b
	*nbufp = *nbufp >= 'A' && *nbufp <= 'Z' ? *nbufp + ('a' - 'A') : *nbufp;
Packit Service ff689b
      name = pool_str2id(pool, nbuf, 0);
Packit Service ff689b
      wp = name ? pool_whatprovides(pool, name) : 0;
Packit Service ff689b
      solv_free(nbuf);
Packit Service ff689b
    }
Packit Service ff689b
  else
Packit Service ff689b
    wp = pool_whatprovides(pool, name);
Packit Service ff689b
  if (wp && evr && evr != 1)
Packit Service ff689b
    {
Packit Service ff689b
      const char *evrstr = pool_id2str(pool, evr);
Packit Service ff689b
      pp = pool->whatprovidesdata + wp;
Packit Service ff689b
      while ((p = *pp++) != 0)
Packit Service ff689b
	{
Packit Service ff689b
	  if (solvable_conda_matchversion(pool->solvables + p, evrstr))
Packit Service ff689b
	    queue_push(plist, p);
Packit Service ff689b
	  else
Packit Service ff689b
	    wp = 0; 
Packit Service ff689b
	}
Packit Service ff689b
    }
Packit Service ff689b
  return wp;
Packit Service ff689b
}
Packit Service ff689b
Packit Service ff689b
/* create a CONDA_REL relation from a matchspec */
Packit Service ff689b
Id
Packit Service ff689b
pool_conda_matchspec(Pool *pool, const char *name)
Packit Service ff689b
{
Packit Service ff689b
  const char *p2;
Packit Service ff689b
  char *name2;
Packit Service ff689b
  char *p, *pp;
Packit Service ff689b
  char *build, *buildend, *version, *versionend;
Packit Service ff689b
  Id nameid, evrid;
Packit Service ff689b
  int haveglob = 0;
Packit Service ff689b
Packit Service ff689b
  /* ignore channel and namespace for now */
Packit Service ff689b
  if ((p2 = strrchr(name, ':')))
Packit Service ff689b
    name = p2 + 1;
Packit Service ff689b
  name2 = solv_strdup(name);
Packit Service ff689b
  /* find end of name */
Packit Service ff689b
  for (p = name2; *p && *p != ' ' && *p != '=' && *p != '<' && *p != '>' && *p != '!' && *p != '~'; p++)
Packit Service ff689b
    {
Packit Service ff689b
      if (*p >= 'A' && *p <= 'Z')
Packit Service ff689b
        *(char *)p += 'a' - 'A';		/* lower case the name */
Packit Service ff689b
      else if (*p == '*')
Packit Service ff689b
	haveglob = 1;
Packit Service ff689b
    }
Packit Service ff689b
  if (p == name2)
Packit Service ff689b
    {
Packit Service ff689b
      solv_free(name2);
Packit Service ff689b
      return 0;	/* error: empty package name */
Packit Service ff689b
    }
Packit Service ff689b
  nameid = pool_strn2id(pool, name2, p - name2, 1);
Packit Service ff689b
  while (*p == ' ')
Packit Service ff689b
    p++;
Packit Service ff689b
  if (!*p)
Packit Service ff689b
    {
Packit Service ff689b
      if (*name2 != '^' && !haveglob)
Packit Service ff689b
	{
Packit Service ff689b
	  solv_free(name2);
Packit Service ff689b
	  return nameid;		/* return a simple dependency if no glob/regex */
Packit Service ff689b
	}
Packit Service ff689b
      evrid = pool_str2id(pool, "*", 1);
Packit Service ff689b
      solv_free(name2);
Packit Service ff689b
      return pool_rel2id(pool, nameid, evrid, REL_CONDA, 1);
Packit Service ff689b
    }
Packit Service ff689b
  /* have version */
Packit Service ff689b
  version = p;
Packit Service ff689b
  versionend = p + strlen(p);
Packit Service ff689b
  while (versionend > version && versionend[-1] == ' ')
Packit Service ff689b
    versionend--;
Packit Service ff689b
  build = buildend = 0;
Packit Service ff689b
  /* split of build */
Packit Service ff689b
  p = versionend;
Packit Service ff689b
  for (;;)
Packit Service ff689b
    {
Packit Service ff689b
      while (p > version && p[-1] != ' ' && p[-1] != '-' && p[-1] != '=' && p[-1] != ',' && p[-1] != '|' && p[-1] != '<' && p[-1] != '>' && p[-1] != '~')
Packit Service ff689b
	p--;
Packit Service ff689b
      if (p <= version + 1 || (p[-1] != ' ' && p[-1] != '='))
Packit Service ff689b
	break;		/* no build */
Packit Service ff689b
      /* check char before delimiter */
Packit Service ff689b
      if (p[-2] == '=' || p[-2] == '!' || p[-2] == '|' || p[-2] == ',' || p[-2] == '<' || p[-2] == '>' || p[-2] == '~')
Packit Service ff689b
	{
Packit Service ff689b
	  /* illegal combination */
Packit Service ff689b
	  if (p[-1] == ' ')
Packit Service ff689b
	    {
Packit Service ff689b
	      p--;
Packit Service ff689b
	      continue;	/* special case space: it may be in the build */
Packit Service ff689b
	    }
Packit Service ff689b
	  break;	/* no build */
Packit Service ff689b
	}
Packit Service ff689b
      if (p < versionend)
Packit Service ff689b
	{
Packit Service ff689b
	  build = p;
Packit Service ff689b
	  buildend = versionend;
Packit Service ff689b
	  versionend = p - 1;
Packit Service ff689b
	}
Packit Service ff689b
      break;
Packit Service ff689b
    }
Packit Service ff689b
  /* do weird version translation */
Packit Service ff689b
  if (versionend > version && version[0] == '=')
Packit Service ff689b
    {
Packit Service ff689b
      if (versionend - version >= 2 && version[1] == '=')
Packit Service ff689b
	{
Packit Service ff689b
	  if (!build)
Packit Service ff689b
	    version += 2;
Packit Service ff689b
	}
Packit Service ff689b
      else if (build)
Packit Service ff689b
	version += 1;
Packit Service ff689b
      else
Packit Service ff689b
	{
Packit Service ff689b
	  for (p = version + 1; p < versionend; p++)
Packit Service ff689b
	    if (*p == '=' || *p == ',' || *p == '|')
Packit Service ff689b
	      break;
Packit Service ff689b
	  if (p == versionend)
Packit Service ff689b
	    {
Packit Service ff689b
	      memmove(version, version + 1, versionend - version - 1);
Packit Service ff689b
	      versionend[-1] = '*';
Packit Service ff689b
	    }
Packit Service ff689b
	}
Packit Service ff689b
    }
Packit Service ff689b
#if 0
Packit Service ff689b
  printf("version: >%.*s<\n", (int)(versionend - version), version);
Packit Service ff689b
  if (build) printf("build: >%.*s<\n", (int)(buildend - build), build);
Packit Service ff689b
#endif
Packit Service ff689b
  /* strip spaces from version */
Packit Service ff689b
  for (p = pp = version; pp < versionend; pp++)
Packit Service ff689b
    if (*pp != ' ')
Packit Service ff689b
      *p++ = *pp;
Packit Service ff689b
  if (build)
Packit Service ff689b
    {
Packit Service ff689b
      *p++ = ' ';
Packit Service ff689b
      memcpy(p, build, buildend - build);
Packit Service ff689b
      p += buildend - build;
Packit Service ff689b
    }
Packit Service ff689b
  evrid = pool_strn2id(pool, version, p - version, 1);
Packit Service ff689b
  solv_free(name2);
Packit Service ff689b
  return pool_rel2id(pool, nameid, evrid, REL_CONDA, 1);
Packit Service ff689b
}
Packit Service ff689b