rules/selection: use strrchr in EVR checks as well
[platform/upstream/libsolv.git] / src / selection.c
index 0128f80..df09b5f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007, Novell Inc.
+ * Copyright (c) 2012, Novell Inc.
  *
  * This program is licensed under the BSD license, read LICENSE.BSD
  * for further information
@@ -20,6 +20,7 @@
 
 #include "selection.h"
 #include "solver.h"
+#include "evr.h"
 
 
 static int
@@ -120,7 +121,7 @@ selection_flatten(Pool *pool, Queue *selection)
 {
   Queue q;
   int i;
-  if (selection->count <= 1)
+  if (selection->count <= 2)
     return;
   for (i = 0; i < selection->count; i += 2)
     if ((selection->elements[i] & SOLVER_SELECTMASK) == SOLVER_SOLVABLE_ALL)
@@ -151,35 +152,130 @@ selection_flatten(Pool *pool, Queue *selection)
 }
 
 static void
-selection_limit_rel(Pool *pool, Queue *selection, Id relflags, Id relevr)
+selection_filter_rel(Pool *pool, Queue *selection, Id relflags, Id relevr)
 {
-  int i, j;
-  for (i = j = 0; i < selection->count; i += 2)
+  int i;
+
+  for (i = 0; i < selection->count; i += 2)
     {
       Id select = selection->elements[i] & SOLVER_SELECTMASK;
       Id id = selection->elements[i + 1];
-      if (select != SOLVER_SOLVABLE_NAME && select != SOLVER_SOLVABLE_PROVIDES)
-       continue;       /* actually internal error */
-      if (relflags == REL_ARCH && (relevr == ARCH_SRC || relevr == ARCH_NOSRC) && ISRELDEP(id))
+      if (select == SOLVER_SOLVABLE || select == SOLVER_SOLVABLE_ONE_OF)
        {
-         Reldep *rd = GETRELDEP(pool, id);
-         if (rd->flags == REL_ARCH && rd->evr == ARCH_SRC)
-           id = rd->name;
+         /* done by selection_addsrc */
+         Queue q;
+         Id p, pp;
+         Id rel = 0, relname = 0;
+         int miss = 0;
+
+         queue_init(&q);
+         FOR_JOB_SELECT(p, pp, select, id)
+           {
+             Solvable *s = pool->solvables + p;
+             if (!rel || s->name != relname)
+               {
+                 relname = s->name;
+                 rel = pool_rel2id(pool, relname, relevr, relflags, 1);
+               }
+             if (pool_match_nevr(pool, s, rel))
+               queue_push(&q, p);
+             else
+               miss = 1;
+           }
+         if (miss)
+           {
+             if (q.count == 1)
+               {
+                 selection->elements[i] = SOLVER_SOLVABLE | SOLVER_NOAUTOSET;
+                 selection->elements[i + 1] = q.elements[0];
+               }
+             else
+               {
+                 selection->elements[i] = SOLVER_SOLVABLE_ONE_OF;
+                 selection->elements[i + 1] = pool_queuetowhatprovides(pool, &q);
+               }
+           }
+         queue_free(&q);
+       }
+      else if (select == SOLVER_SOLVABLE_NAME || select == SOLVER_SOLVABLE_PROVIDES)
+       {
+         /* don't stack src reldeps */
+         if (relflags == REL_ARCH && (relevr == ARCH_SRC || relevr == ARCH_NOSRC) && ISRELDEP(id))
+           {
+             Reldep *rd = GETRELDEP(pool, id);
+             if (rd->flags == REL_ARCH && rd->evr == ARCH_SRC)
+               id = rd->name;
+           }
+         selection->elements[i + 1] = pool_rel2id(pool, id, relevr, relflags, 1);
        }
-      selection->elements[i + 1] = pool_rel2id(pool, id, relevr, relflags, 1);
+      else
+       continue;       /* actually internal error */
       if (relflags == REL_ARCH)
         selection->elements[i] |= SOLVER_SETARCH;
-      if (relflags == REL_EQ && select == SOLVER_SOLVABLE_NAME && selection->elements[i])
+      if (relflags == REL_EQ && select != SOLVER_SOLVABLE_PROVIDES)
         {
          if (pool->disttype == DISTTYPE_DEB)
             selection->elements[i] |= SOLVER_SETEVR;   /* debian can't match version only like rpm */
          else
-           selection->elements[i] |= strchr(pool_id2str(pool, relevr), '-') != 0 ? SOLVER_SETEVR : SOLVER_SETEV;
+           {
+             const char *rel =  strrchr(pool_id2str(pool, relevr), '-');
+             if (rel && pool->disttype == DISTTYPE_HAIKU && (rel[1] < '0' || rel[1] > '9'))
+               rel = 0;
+             selection->elements[i] |= rel ? SOLVER_SETEVR : SOLVER_SETEV;
+           }
         }
     }
   selection_prune(pool, selection);
 }
 
+static void
+selection_addsrc(Pool *pool, Queue *selection, int flags)
+{
+  Queue q;
+  Id p, name;
+  int i, havesrc;
+
+  if ((flags & SELECTION_INSTALLED_ONLY) != 0)
+    return;    /* sources can't be installed */
+  queue_init(&q);
+  for (i = 0; i < selection->count; i += 2)
+    {
+      if (selection->elements[i] != SOLVER_SOLVABLE_NAME)
+       continue;
+      name = selection->elements[i + 1];
+      havesrc = 0;
+      queue_empty(&q);
+      FOR_POOL_SOLVABLES(p)
+       {
+         Solvable *s = pool->solvables + p;
+         if (s->name != name)
+           continue;
+         if (s->arch == ARCH_SRC || s->arch == ARCH_NOSRC)
+           {
+             if (pool_disabled_solvable(pool, s))
+               continue;
+             havesrc = 1;
+           }
+         else if (s->repo != pool->installed && !pool_installable(pool, s))
+           continue;
+         queue_push(&q, p);
+       }
+      if (!havesrc || !q.count)
+       continue;
+      if (q.count == 1)
+       {
+         selection->elements[i] = SOLVER_SOLVABLE | SOLVER_NOAUTOSET;
+         selection->elements[i + 1] = q.elements[0];
+       }
+      else
+       {
+         selection->elements[i] = SOLVER_SOLVABLE_ONE_OF;
+         selection->elements[i + 1] = pool_queuetowhatprovides(pool, &q);
+       }
+    }
+  queue_free(&q);
+}
+
 static int
 selection_depglob(Pool *pool, Queue *selection, const char *name, int flags)
 {
@@ -188,8 +284,11 @@ selection_depglob(Pool *pool, Queue *selection, const char *name, int flags)
   int doglob = 0;
   int globflags = 0;
 
-  if ((flags & SELECTION_SOURCE) != 0)
-    flags &= ~SELECTION_PROVIDES;      /* sources don't provide anything */
+  if ((flags & SELECTION_SOURCE_ONLY) != 0)
+    {
+      flags &= ~SELECTION_PROVIDES;    /* sources don't provide anything */
+      flags &= ~SELECTION_WITH_SOURCE;
+    }
 
   if (!(flags & (SELECTION_NAME|SELECTION_PROVIDES)))
     return 0;
@@ -202,7 +301,7 @@ selection_depglob(Pool *pool, Queue *selection, const char *name, int flags)
       id = pool_str2id(pool, name, 0);
       if (id)
        {
-         if ((flags & SELECTION_SOURCE) != 0 && (flags & SELECTION_NAME) != 0)
+         if ((flags & (SELECTION_SOURCE_ONLY | SELECTION_WITH_SOURCE)) != 0 && (flags & SELECTION_NAME) != 0)
            {
              /* src rpms don't have provides, so we must check every solvable */
              FOR_PROVIDES(p, pp, id)   /* try fast path first */
@@ -212,8 +311,11 @@ selection_depglob(Pool *pool, Queue *selection, const char *name, int flags)
                    {
                      if ((flags & SELECTION_INSTALLED_ONLY) != 0 && s->repo != pool->installed)
                        continue;
-                     id = pool_rel2id(pool, id, ARCH_SRC, REL_ARCH, 1);
+                     if ((flags & SELECTION_SOURCE_ONLY) != 0)
+                       id = pool_rel2id(pool, id, ARCH_SRC, REL_ARCH, 1);
                      queue_push2(selection, SOLVER_SOLVABLE_NAME, id);
+                     if ((flags & SELECTION_WITH_SOURCE) != 0)
+                       selection_addsrc(pool, selection, flags);
                      return SELECTION_NAME;
                    }
                }
@@ -224,8 +326,13 @@ selection_depglob(Pool *pool, Queue *selection, const char *name, int flags)
                    {
                      if ((flags & SELECTION_INSTALLED_ONLY) != 0 && s->repo != pool->installed)
                        continue;       /* just in case... src rpms can't be installed */
-                     id = pool_rel2id(pool, id, ARCH_SRC, REL_ARCH, 1);
+                     if (pool_disabled_solvable(pool, s))
+                       continue;
+                     if ((flags & SELECTION_SOURCE_ONLY) != 0)
+                       id = pool_rel2id(pool, id, ARCH_SRC, REL_ARCH, 1);
                      queue_push2(selection, SOLVER_SOLVABLE_NAME, id);
+                     if ((flags & SELECTION_WITH_SOURCE) != 0)
+                       selection_addsrc(pool, selection, flags);
                      return SELECTION_NAME;
                    }
                }
@@ -238,9 +345,11 @@ selection_depglob(Pool *pool, Queue *selection, const char *name, int flags)
              match = 1;
              if (s->name == id && (flags & SELECTION_NAME) != 0)
                {
-                 if ((flags & SELECTION_SOURCE) != 0)
+                 if ((flags & SELECTION_SOURCE_ONLY) != 0)
                    id = pool_rel2id(pool, id, ARCH_SRC, REL_ARCH, 1);
                  queue_push2(selection, SOLVER_SOLVABLE_NAME, id);
+                 if ((flags & SELECTION_WITH_SOURCE) != 0)
+                   selection_addsrc(pool, selection, flags);
                  return SELECTION_NAME;
                }
            }
@@ -261,7 +370,7 @@ selection_depglob(Pool *pool, Queue *selection, const char *name, int flags)
   if (doglob && (flags & SELECTION_NOCASE) != 0)
     globflags = FNM_CASEFOLD;
 
-#if 0  /* doesn't work with selection_limit_rel yet */
+#if 0  /* doesn't work with selection_filter_rel yet */
   if (doglob && !strcmp(name, "*") && (flags & SELECTION_FLAT) != 0)
     {
       /* can't do this for SELECTION_PROVIDES, as src rpms don't provide anything */
@@ -279,15 +388,19 @@ selection_depglob(Pool *pool, Queue *selection, const char *name, int flags)
       FOR_POOL_SOLVABLES(p)
         {
           Solvable *s = pool->solvables + p;
-          if (!pool_installable(pool, s))
-           if (!(flags & SELECTION_SOURCE) || (s->arch != ARCH_SRC && s->arch != ARCH_NOSRC))
-              continue;
+          if (s->repo != pool->installed && !pool_installable(pool, s))
+           {
+             if (!(flags & SELECTION_SOURCE_ONLY) || (s->arch != ARCH_SRC && s->arch != ARCH_NOSRC))
+                continue;
+             if (pool_disabled_solvable(pool, s))
+               continue;
+           }
          if ((flags & SELECTION_INSTALLED_ONLY) != 0 && s->repo != pool->installed)
            continue;
           id = s->name;
           if ((doglob ? fnmatch(name, pool_id2str(pool, id), globflags) : strcasecmp(name, pool_id2str(pool, id))) == 0)
             {
-             if ((flags & SELECTION_SOURCE) != 0)
+             if ((flags & SELECTION_SOURCE_ONLY) != 0)
                id = pool_rel2id(pool, id, ARCH_SRC, REL_ARCH, 1);
              /* queue_pushunique2 */
               for (i = 0; i < selection->count; i += 2)
@@ -299,7 +412,11 @@ selection_depglob(Pool *pool, Queue *selection, const char *name, int flags)
             }
         }
       if (match)
-        return SELECTION_NAME;
+       {
+         if ((flags & SELECTION_WITH_SOURCE) != 0)
+           selection_addsrc(pool, selection, flags);
+          return SELECTION_NAME;
+       }
     }
   if ((flags & SELECTION_PROVIDES))
     {
@@ -337,18 +454,20 @@ selection_depglob_arch(Pool *pool, Queue *selection, const char *name, int flags
 
   if ((ret = selection_depglob(pool, selection, name, flags)) != 0)
     return ret;
-  /* check if theres an .arch suffix */
+  if (!(flags & SELECTION_DOTARCH))
+    return 0;
+  /* check if there is an .arch suffix */
   if ((r = strrchr(name, '.')) != 0 && r[1] && (archid = str2archid(pool, r + 1)) != 0)
     {
       char *rname = solv_strdup(name);
       rname[r - name] = 0;
       if (archid == ARCH_SRC || archid == ARCH_NOSRC)
-       flags |= SELECTION_SOURCE;
+       flags |= SELECTION_SOURCE_ONLY;
       if ((ret = selection_depglob(pool, selection, rname, flags)) != 0)
        {
-         selection_limit_rel(pool, selection, REL_ARCH, archid);
+         selection_filter_rel(pool, selection, REL_ARCH, archid);
          solv_free(rname);
-         return ret;
+         return ret | SELECTION_DOTARCH;
        }
       solv_free(rname);
     }
@@ -372,9 +491,13 @@ selection_filelist(Pool *pool, Queue *selection, const char *name, int flags)
       Solvable *s = pool->solvables + di.solvid;
       if (!s->repo)
        continue;
-      if (!pool_installable(pool, s))
-       if (!(flags & SELECTION_SOURCE) || (s->arch != ARCH_SRC && s->arch != ARCH_NOSRC))
-         continue;
+      if (s->repo != pool->installed && !pool_installable(pool, s))
+       {
+         if (!(flags & SELECTION_SOURCE_ONLY) || (s->arch != ARCH_SRC && s->arch != ARCH_NOSRC))
+           continue;
+         if (pool_disabled_solvable(pool, s))
+           continue;
+       }
       if ((flags & SELECTION_INSTALLED_ONLY) != 0 && s->repo != pool->installed)
        continue;
       queue_push(&q, di.solvid);
@@ -405,6 +528,12 @@ selection_rel(Pool *pool, Queue *selection, const char *name, int flags)
   if ((r = strpbrk(rname, "<=>")) != 0)
     {
       int nend = r - rname;
+      if (nend && *r == '=' && r[-1] == '!')
+       {
+         nend--;
+         r++;
+         rflags = REL_LT|REL_GT;
+       }
       for (; *r; r++)
         {
           if (*r == '<')
@@ -430,16 +559,106 @@ selection_rel(Pool *pool, Queue *selection, const char *name, int flags)
   if ((ret = selection_depglob_arch(pool, selection, rname, flags)) != 0)
     {
       if (rflags)
-       selection_limit_rel(pool, selection, rflags, pool_str2id(pool, r, 1));
+       selection_filter_rel(pool, selection, rflags, pool_str2id(pool, r, 1));
       solv_free(rname);
-      return ret;
+      return ret | SELECTION_REL;
     }
   solv_free(rname);
   return 0;
 }
 
+#if defined(MULTI_SEMANTICS)
+# define EVRCMP_DEPCMP (pool->disttype == DISTTYPE_DEB ? EVRCMP_COMPARE : EVRCMP_MATCH_RELEASE)
+#elif defined(DEBIAN)
+# define EVRCMP_DEPCMP EVRCMP_COMPARE
+#else
+# define EVRCMP_DEPCMP EVRCMP_MATCH_RELEASE
+#endif
+
+/* magic epoch promotion code, works only for SELECTION_NAME selections */
+static void
+selection_filter_evr(Pool *pool, Queue *selection, char *evr)
+{
+  int i, j;
+  Queue q;
+  Id qbuf[10];
+
+  queue_init(&q);
+  queue_init_buffer(&q, qbuf, sizeof(qbuf)/sizeof(*qbuf));
+  for (i = j = 0; i < selection->count; i += 2)
+    {
+      Id select = selection->elements[i] & SOLVER_SELECTMASK;
+      Id id = selection->elements[i + 1];
+      Id p, pp;
+      const char *lastepoch = 0;
+      int lastepochlen = 0;
+
+      queue_empty(&q);
+      FOR_JOB_SELECT(p, pp, select, id)
+       {
+         Solvable *s = pool->solvables + p;
+         const char *sevr = pool_id2str(pool, s->evr);
+         const char *sp;
+         for (sp = sevr; *sp >= '0' && *sp <= '9'; sp++)
+           ;
+         if (*sp != ':')
+           sp = sevr;
+         /* compare vr part */
+         if (strcmp(evr, sp != sevr ? sp + 1 : sevr) != 0)
+           {
+             int r = pool_evrcmp_str(pool, sp != sevr ? sp + 1 : sevr, evr, EVRCMP_DEPCMP);
+             if (r == -1 || r == 1)
+               continue;       /* solvable does not match vr */
+           }
+         queue_push(&q, p);
+         if (sp > sevr)
+           {
+             while (sevr < sp && *sevr == '0') /* normalize epoch */
+               sevr++;
+           }
+         if (!lastepoch)
+           {
+             lastepoch = sevr;
+             lastepochlen = sp - sevr;
+           }
+         else if (lastepochlen != sp - sevr || strncmp(lastepoch, sevr, lastepochlen) != 0)
+           lastepochlen = -1;  /* multiple different epochs */
+       }
+      if (!lastepoch || lastepochlen == 0)
+       id = pool_str2id(pool, evr, 1);         /* no match at all or zero epoch */
+      else if (lastepochlen >= 0)
+       {
+         /* found exactly one epoch, simply prepend */
+         char *evrx = solv_malloc(strlen(evr) + lastepochlen + 2);
+         strncpy(evrx, lastepoch, lastepochlen + 1);
+         strcpy(evrx + lastepochlen + 1, evr);
+         id = pool_str2id(pool, evrx, 1);
+         solv_free(evrx);
+       }
+      else
+       {
+         /* multiple epochs in multiple solvables, convert to list of solvables */
+         selection->elements[j] = (selection->elements[i] & ~SOLVER_SELECTMASK) | SOLVER_SOLVABLE_ONE_OF;
+         selection->elements[j + 1] = pool_queuetowhatprovides(pool, &q);
+         j += 2;
+         continue;
+       }
+      queue_empty(&q);
+      queue_push2(&q, selection->elements[i], selection->elements[i + 1]);
+      selection_filter_rel(pool, &q, REL_EQ, id);
+      if (!q.count)
+        continue;              /* oops, no match */
+      selection->elements[j] = q.elements[0];
+      selection->elements[j + 1] = q.elements[1];
+      j += 2;
+    }
+  queue_truncate(selection, j);
+  queue_free(&q);
+}
+
+/* match the "canonical" name of the package */
 static int
-selection_nevra(Pool *pool, Queue *selection, const char *name, int flags)
+selection_canon(Pool *pool, Queue *selection, const char *name, int flags)
 {
   char *rname, *r, *r2;
   Id archid = 0;
@@ -467,14 +686,37 @@ selection_nevra(Pool *pool, Queue *selection, const char *name, int flags)
          return 0;
        }
       /* is there a vaild arch? */
-      if ((r2 = strchr(r, '_')) != 0 && r[1] && (archid = str2archid(pool, r + 1)) != 0)
+      if ((r2 = strrchr(r, '_')) != 0 && r[1] && (archid = str2archid(pool, r + 1)) != 0)
+       {
+         *r2 = 0;      /* split off */
+          selection_filter_rel(pool, selection, REL_ARCH, archid);
+       }
+      selection_filter_rel(pool, selection, REL_EQ, pool_str2id(pool, r, 1));
+      solv_free(rname);
+      return ret | SELECTION_CANON;
+    }
+
+  if (pool->disttype == DISTTYPE_HAIKU)
+    {
+      if ((r = strchr(name, '-')) == 0)
+       return 0;
+      rname = solv_strdup(name);       /* so we can modify it */
+      r = rname + (r - name);
+      *r++ = 0;
+      if ((ret = selection_depglob(pool, selection, rname, flags)) == 0)
+       {
+         solv_free(rname);
+         return 0;
+       }
+      /* is there a vaild arch? */
+      if ((r2 = strrchr(r, '-')) != 0 && r[1] && (archid = str2archid(pool, r + 1)) != 0)
        {
          *r2 = 0;      /* split off */
-          selection_limit_rel(pool, selection, REL_ARCH, archid);
+          selection_filter_rel(pool, selection, REL_ARCH, archid);
        }
-      selection_limit_rel(pool, selection, REL_EQ, pool_str2id(pool, r, 1));
+      selection_filter_rel(pool, selection, REL_EQ, pool_str2id(pool, r, 1));
       solv_free(rname);
-      return ret;
+      return ret | SELECTION_CANON;
     }
 
   if ((r = strrchr(name, '-')) == 0)
@@ -487,7 +729,7 @@ selection_nevra(Pool *pool, Queue *selection, const char *name, int flags)
   if ((r2 = strrchr(r + 1, '.')) != 0 && r2[1] && (archid = str2archid(pool, r2 + 1)) != 0)
     *r2 = 0;   /* found valid arch, split it off */
   if (archid == ARCH_SRC || archid == ARCH_NOSRC)
-    flags |= SELECTION_SOURCE;
+    flags |= SELECTION_SOURCE_ONLY;
 
   /* try with just the version */
   if ((ret = selection_depglob(pool, selection, rname, flags)) == 0)
@@ -508,10 +750,10 @@ selection_nevra(Pool *pool, Queue *selection, const char *name, int flags)
        }
     }
   if (archid)
-    selection_limit_rel(pool, selection, REL_ARCH, archid);
-  selection_limit_rel(pool, selection, REL_EQ, pool_str2id(pool, r + 1, 1));
+    selection_filter_rel(pool, selection, REL_ARCH, archid);
+  selection_filter_evr(pool, selection, r + 1);        /* magic epoch promotion */
   solv_free(rname);
-  return ret;
+  return ret | SELECTION_CANON;
 }
 
 int
@@ -523,19 +765,21 @@ selection_make(Pool *pool, Queue *selection, const char *name, int flags)
   queue_empty(selection);
   if (*name == '/' && (flags & SELECTION_FILELIST))
     ret = selection_filelist(pool, selection, name, flags);
-  if (!ret && (r = strpbrk(name, "<=>")) != 0)
+  if (!ret && (flags & SELECTION_REL) != 0 && (r = strpbrk(name, "<=>")) != 0)
     ret = selection_rel(pool, selection, name, flags);
   if (!ret)
     ret = selection_depglob_arch(pool, selection, name, flags);
-  if (!ret && (flags & SELECTION_NAME) != 0)
-    ret = selection_nevra(pool, selection, name, flags);
+  if (!ret && (flags & SELECTION_CANON) != 0)
+    ret = selection_canon(pool, selection, name, flags);
+  if (ret && !selection->count)
+    ret = 0;   /* no match -> always return zero */
   if (ret && (flags & SELECTION_FLAT) != 0)
     selection_flatten(pool, selection);
   return ret;
 }
 
 void
-selection_limit(Pool *pool, Queue *sel1, Queue *sel2)
+selection_filter(Pool *pool, Queue *sel1, Queue *sel2)
 {
   int i, j, miss;
   Id p, pp;