- start support for Fedora comps format
authorMichael Schroeder <mls@suse.de>
Thu, 2 Feb 2012 15:30:41 +0000 (16:30 +0100)
committerMichael Schroeder <mls@suse.de>
Thu, 2 Feb 2012 15:30:41 +0000 (16:30 +0100)
CMakeLists.txt
ext/CMakeLists.txt
ext/libsolvext.ver
ext/repo_comps.c [new file with mode: 0644]
ext/repo_comps.h [new file with mode: 0644]
src/pool.h
src/poolid.c
tools/CMakeLists.txt
tools/comps2solv.c [new file with mode: 0644]

index 897438827f539d2ba1f7167664369b8543b27c0a..fd5f75ee500bebdb9188a0b9e528de76fb78ee75 100644 (file)
@@ -14,6 +14,7 @@ OPTION (USE_VENDORDIRS "Install the bindings in vendor directories?" OFF)
 OPTION (ENABLE_RPMDB "Build with rpm database support?" OFF)
 OPTION (ENABLE_RPMMD "Build with rpmmd repository support?" OFF)
 OPTION (ENABLE_SUSEREPO "Build with suse repository support?" OFF)
+OPTION (ENABLE_COMPS "Build with fedora comps support?" OFF)
 OPTION (ENABLE_HELIXREPO "Build with helix repository support?" OFF)
 OPTION (ENABLE_DEBIAN "Build with debian database/repository support?" OFF)
 
@@ -128,7 +129,7 @@ TEST_BIG_ENDIAN (WORDS_BIGENDIAN)
 
 # should create config.h with #cmakedefine instead...
 FOREACH (VAR HAVE_STRCHRNUL HAVE_FOPENCOOKIE HAVE_FUNOPEN WORDS_BIGENDIAN
-  ENABLE_RPMDB ENABLE_RPMMD ENABLE_SUSEREPO ENABLE_HELIXREPO ENABLE_DEBIAN)
+  ENABLE_RPMDB ENABLE_RPMMD ENABLE_SUSEREPO ENABLE_COMPS ENABLE_HELIXREPO ENABLE_DEBIAN)
   IF(${VAR})
     ADD_DEFINITIONS (-D${VAR}=1)
   ENDIF (${VAR})
index b88b7b287271edd296266cbd882d321a25fc0366..9fc903427cf74d9dbb5eb381d40fbdea2a4f305b 100644 (file)
@@ -29,6 +29,13 @@ IF (ENABLE_SUSEREPO)
        repo_susetags.h repo_zyppdb.h)
 ENDIF (ENABLE_SUSEREPO)
 
+IF (ENABLE_COMPS)
+    SET (libsolvext_SRCS ${libsolvext_SRCS}
+       repo_comps.c)
+    SET (libsolvext_HEADERS ${libsolvext_HEADERS}
+       repo_comps.h)
+ENDIF (ENABLE_COMPS)
+
 IF (ENABLE_DEBIAN)
     SET (libsolvext_SRCS ${libsolvext_SRCS}
        repo_deb.c)
index cc33da2fe145ec0cbd1260409e164ee25a7a0726..c7bda4841dce9a97073f04a24769b3efb4779892 100644 (file)
@@ -3,6 +3,7 @@ SOLV_1.0 {
                pool_findfileconflicts;
                repo_add_code11_products;
                repo_add_content;
+               repo_add_comps;
                repo_add_deb;
                repo_add_debdb;
                repo_add_debpackages;
diff --git a/ext/repo_comps.c b/ext/repo_comps.c
new file mode 100644 (file)
index 0000000..7a100a7
--- /dev/null
@@ -0,0 +1,418 @@
+/*
+ * repo_comps.c
+ *
+ * Parses RedHat comps format
+ *
+ * Copyright (c) 2012, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <dirent.h>
+#include <expat.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "util.h"
+#define DISABLE_SPLIT
+#include "tools_util.h"
+#include "repo_comps.h"
+
+/*
+ * TODO:
+ *
+ * what's the difference between group/category?
+ * handle "default" and "langonly".
+ *
+ * maybe handle REL_COND in solver recommends handling?
+ */
+
+enum state {
+  STATE_START,
+  STATE_COMPS,
+  STATE_GROUP,
+  STATE_ID,
+  STATE_NAME,
+  STATE_DESCRIPTION,
+  STATE_DISPLAY_ORDER,
+  STATE_DEFAULT,
+  STATE_LANGONLY,
+  STATE_LANG_ONLY,
+  STATE_USERVISIBLE,
+  STATE_PACKAGELIST,
+  STATE_PACKAGEREQ,
+  STATE_CATEGORY,
+  STATE_CID,
+  STATE_CNAME,
+  STATE_CDESCRIPTION,
+  STATE_CDISPLAY_ORDER,
+  STATE_GROUPLIST,
+  STATE_GROUPID,
+  NUMSTATES
+};
+
+struct stateswitch {
+  enum state from;
+  char *ename;
+  enum state to;
+  int docontent;
+};
+
+/* must be sorted by first column */
+static struct stateswitch stateswitches[] = {
+  { STATE_START,       "comps",         STATE_COMPS,         0 },
+  { STATE_COMPS,       "group",         STATE_GROUP,         0 },
+  { STATE_COMPS,       "category",      STATE_CATEGORY,      0 },
+  { STATE_GROUP,       "id",            STATE_ID,            1 },
+  { STATE_GROUP,       "name",          STATE_NAME,          1 },
+  { STATE_GROUP,       "description",   STATE_DESCRIPTION,   1 },
+  { STATE_GROUP,       "uservisible",   STATE_USERVISIBLE,   1 },
+  { STATE_GROUP,       "display_order", STATE_DISPLAY_ORDER, 1 },
+  { STATE_GROUP,       "default",       STATE_DEFAULT,       1 },
+  { STATE_GROUP,       "langonly",      STATE_LANGONLY,      1 },
+  { STATE_GROUP,       "lang_only",     STATE_LANG_ONLY,     1 },
+  { STATE_GROUP,       "packagelist",   STATE_PACKAGELIST,   0 },
+  { STATE_PACKAGELIST, "packagereq",    STATE_PACKAGEREQ,    1 },
+  { STATE_CATEGORY,    "id",            STATE_CID,           1 },
+  { STATE_CATEGORY,    "name",          STATE_CNAME,         1 },
+  { STATE_CATEGORY,    "description",   STATE_CDESCRIPTION,  1 },
+  { STATE_CATEGORY ,   "grouplist",     STATE_GROUPLIST,     0 },
+  { STATE_CATEGORY ,   "display_order", STATE_CDISPLAY_ORDER, 1 },
+  { STATE_GROUPLIST,   "groupid",       STATE_GROUPID,       1 },
+  { NUMSTATES }
+};
+
+struct parsedata {
+  const char *filename;
+  const char *basename;
+  int depth;
+  enum state state;
+  int statedepth;
+  char *content;
+  int lcontent;
+  int acontent;
+  int docontent;
+  Pool *pool;
+  Repo *repo;
+  Repodata *data;
+
+  struct stateswitch *swtab[NUMSTATES];
+  enum state sbtab[NUMSTATES];
+
+  const char *tmplang;
+  Id reqtype;
+  Id condreq;
+
+  Solvable *solvable;
+  Id handle;
+
+  Id langcache[ID_NUM_INTERNAL];
+};
+
+
+/*
+ * find_attr
+ * find value for xml attribute
+ * I: txt, name of attribute
+ * I: atts, list of key/value attributes
+ * I: dup, strdup it
+ * O: pointer to value of matching key, or NULL
+ *
+ */
+
+static inline const char *
+find_attr(const char *txt, const char **atts, int dup)
+{
+  for (; *atts; atts += 2)
+    {
+      if (!strcmp(*atts, txt))
+        return dup ? solv_strdup(atts[1]) : atts[1];
+    }
+  return 0;
+}
+
+
+/*
+ * create localized tag
+ */
+
+static Id
+langtag(struct parsedata *pd, Id tag, const char *language)
+{
+  if (language && !language[0])
+    language = 0;
+  if (!language || tag >= ID_NUM_INTERNAL || 1)
+    return pool_id2langid(pd->repo->pool, tag, language, 1);
+  if (!pd->langcache[tag])
+    pd->langcache[tag] = pool_id2langid(pd->repo->pool, tag, language, 1);
+  return pd->langcache[tag];
+}
+
+
+/*
+ * XML callback: startElement
+ */
+
+static void XMLCALL
+startElement(void *userData, const char *name, const char **atts)
+{
+  struct parsedata *pd = userData;
+  Pool *pool = pd->pool;
+  Solvable *s = pd->solvable;
+  struct stateswitch *sw;
+
+#if 0
+      fprintf(stderr, "start: [%d]%s\n", pd->state, name);
+#endif
+  if (pd->depth != pd->statedepth)
+    {
+      pd->depth++;
+      return;
+    }
+
+  pd->depth++;
+  if (!pd->swtab[pd->state])   /* no statetable -> no substates */
+    {
+#if 0
+      fprintf(stderr, "into unknown: %s (from: %d)\n", name, pd->state);
+#endif
+      return;
+    }
+  for (sw = pd->swtab[pd->state]; sw->from == pd->state; sw++)  /* find name in statetable */
+    if (!strcmp(sw->ename, name))
+      break;
+
+  if (sw->from != pd->state)
+    {
+#if 0
+      fprintf(stderr, "into unknown: %s (from: %d)\n", name, pd->state);
+#endif
+      return;
+    }
+  pd->state = sw->to;
+  pd->docontent = sw->docontent;
+  pd->statedepth = pd->depth;
+  pd->lcontent = 0;
+  *pd->content = 0;
+
+  switch(pd->state)
+    {
+    case STATE_GROUP:
+    case STATE_CATEGORY:
+      s = pd->solvable = pool_id2solvable(pool, repo_add_solvable(pd->repo));
+      pd->handle = s - pool->solvables;
+      break;
+
+    case STATE_NAME:
+    case STATE_CNAME:
+    case STATE_DESCRIPTION:
+    case STATE_CDESCRIPTION:
+      pd->tmplang = find_attr("xml:lang", atts, 1);
+      break;
+
+    case STATE_PACKAGEREQ:
+      {
+       const char *type = find_attr("type", atts, 0);
+       pd->condreq = 0;
+       pd->reqtype = SOLVABLE_RECOMMENDS;
+       if (type && !strcmp(type, "conditional"))
+         {
+           const char *requires = find_attr("requires", atts, 0);
+           if (requires && *requires)
+             pd->condreq = pool_str2id(pool, requires, 1);
+         }
+       else if (type && !strcmp(type, "mandatory"))
+         pd->reqtype = SOLVABLE_REQUIRES;
+       else if (type && !strcmp(type, "optional"))
+         pd->reqtype = SOLVABLE_SUGGESTS;
+       break;
+      }
+
+    default:
+      break;
+    }
+}
+
+
+static void XMLCALL
+endElement(void *userData, const char *name)
+{
+  struct parsedata *pd = userData;
+  Solvable *s = pd->solvable;
+  Id id;
+
+#if 0
+      fprintf(stderr, "end: [%d]%s\n", pd->state, name);
+#endif
+  if (pd->depth != pd->statedepth)
+    {
+      pd->depth--;
+#if 0
+      fprintf(stderr, "back from unknown %d %d %d\n", pd->state, pd->depth, pd->statedepth);
+#endif
+      return;
+    }
+
+  pd->depth--;
+  pd->statedepth--;
+
+  switch (pd->state)
+    {
+    case STATE_GROUP:
+    case STATE_CATEGORY:
+      if (!s->arch)
+       s->arch = ARCH_NOARCH;
+      if (!s->evr)
+       s->evr = ID_EMPTY;
+      if (s->name && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
+       s->provides = repo_addid_dep(pd->repo, s->provides, pool_rel2id(pd->pool, s->name, s->evr, REL_EQ, 1), 0);
+      pd->solvable = 0;
+      break;
+
+    case STATE_ID:
+    case STATE_CID:
+      s->name = pool_str2id(pd->pool, join2("pattern", ":", pd->content), 1);
+      break;
+
+    case STATE_NAME:
+    case STATE_CNAME:
+      repodata_set_str(pd->data, pd->handle, langtag(pd, SOLVABLE_SUMMARY, pd->tmplang), pd->content);
+      pd->tmplang = solv_free((void *)pd->tmplang);
+      break;
+
+    case STATE_DESCRIPTION:
+    case STATE_CDESCRIPTION:
+      repodata_set_str(pd->data, pd->handle, langtag(pd, SOLVABLE_DESCRIPTION, pd->tmplang), pd->content);
+      pd->tmplang = solv_free((void *)pd->tmplang);
+      break;
+
+    case STATE_PACKAGEREQ:
+      id = pool_str2id(pd->pool, pd->content, 1);
+      if (pd->condreq)
+       id = pool_rel2id(pd->pool, id, pd->condreq, REL_COND, 1);
+      repo_add_idarray(pd->repo, pd->handle, pd->reqtype, id);
+      break;
+
+    case STATE_GROUPID:
+      id = pool_str2id(pd->pool, join2("pattern", ":", pd->content), 1);
+      s->requires = repo_addid_dep(pd->repo, s->requires, id, 0);
+      break;
+
+    case STATE_USERVISIBLE:
+      repodata_set_void(pd->data, pd->handle, SOLVABLE_ISVISIBLE);
+      break;
+
+    case STATE_DISPLAY_ORDER:
+    case STATE_CDISPLAY_ORDER:
+      repodata_set_str(pd->data, pd->handle, SOLVABLE_ORDER, pd->content);
+      break;
+
+    case STATE_DEFAULT:
+      break;
+
+    case STATE_LANGONLY:
+    case STATE_LANG_ONLY:
+      break;
+
+    default:
+      break;
+    }
+
+  pd->state = pd->sbtab[pd->state];
+  pd->docontent = 0;
+
+#if 0
+      fprintf(stderr, "end: [%s] -> %d\n", name, pd->state);
+#endif
+}
+
+
+static void XMLCALL
+characterData(void *userData, const XML_Char *s, int len)
+{
+  struct parsedata *pd = userData;
+  int l;
+  char *c;
+  if (!pd->docontent)
+    return;
+  l = pd->lcontent + len + 1;
+  if (l > pd->acontent)
+    {
+      pd->content = solv_realloc(pd->content, l + 256);
+      pd->acontent = l + 256;
+    }
+  c = pd->content + pd->lcontent;
+  pd->lcontent += len;
+  while (len-- > 0)
+    *c++ = *s++;
+  *c = 0;
+}
+
+#define BUFF_SIZE 8192
+
+
+int
+repo_add_comps(Repo *repo, FILE *fp, int flags)
+{
+  Repodata *data;
+  struct parsedata pd;
+  char buf[BUFF_SIZE];
+  int i, l;
+  struct stateswitch *sw;
+  XML_Parser parser;
+
+  data = repo_add_repodata(repo, flags);
+
+  memset(&pd, 0, sizeof(pd));
+  pd.repo = repo;
+  pd.pool = repo->pool;
+  pd.data = data;
+
+  pd.content = solv_malloc(256);
+  pd.acontent = 256;
+
+  for (i = 0, sw = stateswitches; sw->from != NUMSTATES; i++, sw++)
+    {
+      if (!pd.swtab[sw->from])
+        pd.swtab[sw->from] = sw;
+      pd.sbtab[sw->to] = sw->from;
+    }
+
+  parser = XML_ParserCreate(NULL);
+  XML_SetUserData(parser, &pd);
+  XML_SetElementHandler(parser, startElement, endElement);
+  XML_SetCharacterDataHandler(parser, characterData);
+  for (;;)
+    {
+      l = fread(buf, 1, sizeof(buf), fp);
+      if (XML_Parse(parser, buf, l, l == 0) == XML_STATUS_ERROR)
+       {
+         pool_debug(pd.pool, SOLV_ERROR, "%s at line %u:%u\n", XML_ErrorString(XML_GetErrorCode(parser)), (unsigned int)XML_GetCurrentLineNumber(parser), (unsigned int)XML_GetCurrentColumnNumber(parser));
+         break;
+       }
+      if (l == 0)
+       break;
+    }
+  XML_ParserFree(parser);
+
+  solv_free((void *)pd.tmplang);
+  solv_free(pd.content);
+  join_freemem();
+
+  if (!(flags & REPO_NO_INTERNALIZE))
+    repodata_internalize(data);
+  return 0;
+}
+
+/* EOF */
diff --git a/ext/repo_comps.h b/ext/repo_comps.h
new file mode 100644 (file)
index 0000000..8998b07
--- /dev/null
@@ -0,0 +1,8 @@
+/*
+ * Copyright (c) 2012, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+extern int repo_add_comps(Repo *repo, FILE *fp, int flags);
index 4c1245d207e8eba8dd3e8bfe1d4089b854d0dbcc..2b51167b1c3ddd4e7cddaadb1baa85211a7a3b4f 100644 (file)
@@ -177,6 +177,7 @@ struct _Pool {
 #define REL_NAMESPACE  19
 #define REL_ARCH       20
 #define REL_FILECONFLICT       21
+#define REL_COND       22
 
 #if !defined(__GNUC__) && !defined(__attribute__)
 # define __attribute__(x)
index accccc7b6dda5c8445562cfa257c118e290bee99..b4a51739d7bdc8da3a14b2a70cb281174cfc5d28 100644 (file)
@@ -178,6 +178,8 @@ pool_id2rel(const Pool *pool, Id id)
       return ".";
     case REL_FILECONFLICT:
       return " FILECONFLICT ";
+    case REL_COND:
+      return " IF ";
     default:
       break;
     }
index e2d9a9437dee47a59307296cc68fcc1eebf3e745..5afb5b2c3b5983c09524cbea9ee79c0e1d897046 100644 (file)
@@ -55,6 +55,13 @@ TARGET_LINK_LIBRARIES (susetags2solv toolstuff libsolvext libsolv ${SYSTEM_LIBRA
 SET (tools_list ${tools_list} susetags2solv)
 ENDIF (ENABLE_SUSEREPO)
 
+IF (ENABLE_COMPS)
+ADD_EXECUTABLE (comps2solv comps2solv.c)
+TARGET_LINK_LIBRARIES (comps2solv toolstuff libsolvext libsolv ${SYSTEM_LIBRARIES})
+
+SET (tools_list ${tools_list} comps2solv)
+ENDIF (ENABLE_COMPS)
+
 ADD_EXECUTABLE (installcheck installcheck.c)
 TARGET_LINK_LIBRARIES (installcheck libsolvext libsolv ${SYSTEM_LIBRARIES})
 
diff --git a/tools/comps2solv.c b/tools/comps2solv.c
new file mode 100644 (file)
index 0000000..1e69e9b
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2012, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * comps2solv.c
+ * 
+ * parse Fedora Comps type xml and write out .solv file
+ *
+ * reads from stdin
+ * writes to stdout
+ */
+
+#include <sys/types.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "repo_comps.h"
+#include "common_write.h"
+
+int
+main(int argc, char **argv)
+{
+  Pool *pool = pool_create();
+  Repo *repo = repo_create(pool, "<stdin>");
+  repo_add_comps(repo, stdin, 0);
+  tool_write(repo, 0, 0);
+  pool_free(pool);
+  exit(0);
+}