--- /dev/null
+/*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+#define DO_ARRAY 1
+
+#define _GNU_SOURCE
+#include <sys/types.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <expat.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "repo_diskusagexml.h"
+
+//#define DUMPOUT 0
+
+/*
+ * <diskusage>
+ * <duinfo name="3ddiag" ver="0.742" rel="45" arch="i586">
+ * <dirs>
+ * <dir name="/" size="56" count="11"/>
+ * <dir name="usr/" size="56" count="11"/>
+ * <dir name="usr/bin/" size="38" count="10"/>
+ * <dir name="usr/share/" size="18" count="1"/>
+ * <dir name="usr/share/doc/" size="18" count="1"/>
+ * </dirs>
+ * </duinfo>
+ * <duinfo name="915resolution" ver="0.5.3" rel="74" arch="i586">
+ * <dirs>
+ * <dir name="/" size="27" count="7"/>
+ * ...
+ * </dirs>
+ * </duinfo>
+ * </diskusage>
+ */
+
+enum state {
+ STATE_START,
+ STATE_DISKUSAGE, /* 1 */
+ STATE_DUINFO, /* 2 */
+ STATE_DIRS, /* 3 */
+ STATE_DIR, /* 4 */
+ 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, "diskusage", STATE_DISKUSAGE, 0 },
+ { STATE_DISKUSAGE, "duinfo", STATE_DUINFO, 0 },
+ { STATE_DUINFO, "dirs", STATE_DIRS, 0 },
+ { STATE_DIRS, "dir", STATE_DIR, 0 },
+ { NUMSTATES }
+};
+
+struct parsedata {
+ int depth;
+ enum state state;
+ int statedepth;
+ char *content;
+ int lcontent;
+ int acontent;
+ int docontent;
+ Pool *pool;
+ Repo *repo;
+ Repodata *data;
+ int datanum;
+
+ struct stateswitch *swtab[NUMSTATES];
+ enum state sbtab[NUMSTATES];
+ char *tempstr;
+ int ltemp;
+ int atemp;
+ Solvable *s;
+ Id handle;
+
+ Id (*dirs)[3]; // dirid, size, nfiles
+ int ndirs;
+};
+
+/*
+ * id3_cmp
+ * compare
+ *
+ */
+
+static int
+id3_cmp (const void *v1, const void *v2)
+{
+ Id *i1 = (Id*)v1;
+ Id *i2 = (Id*)v2;
+ return i1[0] - i2[0];
+}
+
+
+/*
+ * commit_diskusage
+ *
+ */
+
+static void
+commit_diskusage (struct parsedata *pd, unsigned handle)
+{
+ unsigned i;
+ Dirpool *dp = &pd->data->dirpool;
+ /* Now sort in dirid order. This ensures that parents come before
+ their children. */
+ if (pd->ndirs > 1)
+ qsort(pd->dirs, pd->ndirs, sizeof (pd->dirs[0]), id3_cmp);
+ /* Substract leaf numbers from all parents to make the numbers
+ non-cumulative. This must be done post-order (i.e. all leafs
+ adjusted before parents). We ensure this by starting at the end of
+ the array moving to the start, hence seeing leafs before parents. */
+ for (i = pd->ndirs; i--;)
+ {
+ unsigned p = dirpool_parent(dp, pd->dirs[i][0]);
+ unsigned j = i;
+ for (; p; p = dirpool_parent(dp, p))
+ {
+ for (; j--;)
+ if (pd->dirs[j][0] == p)
+ break;
+ if (j < pd->ndirs)
+ {
+ if (pd->dirs[j][1] < pd->dirs[i][1])
+ pd->dirs[j][1] = 0;
+ else
+ pd->dirs[j][1] -= pd->dirs[i][1];
+ if (pd->dirs[j][2] < pd->dirs[i][2])
+ pd->dirs[j][2] = 0;
+ else
+ pd->dirs[j][2] -= pd->dirs[i][2];
+ }
+ else
+ /* Haven't found this parent in the list, look further if
+ we maybe find the parents parent. */
+ j = i;
+ }
+ }
+#if 0
+ char sbuf[1024];
+ char *buf = sbuf;
+ unsigned slen = sizeof (sbuf);
+ for (i = 0; i < pd->ndirs; i++)
+ {
+ dir2str (attr, pd->dirs[i][0], &buf, &slen);
+ fprintf (stderr, "have dir %d %d %d %s\n", pd->dirs[i][0], pd->dirs[i][1], pd->dirs[i][2], buf);
+ }
+ if (buf != sbuf)
+ free (buf);
+#endif
+ for (i = 0; i < pd->ndirs; i++)
+ if (pd->dirs[i][1] || pd->dirs[i][2])
+ {
+ repodata_add_dirnumnum(pd->data, handle, SOLVABLE_DISKUSAGE, pd->dirs[i][0], pd->dirs[i][1], pd->dirs[i][2]);
+ }
+ pd->ndirs = 0;
+}
+
+
+/*
+ * find attribute
+ */
+
+static const char *
+find_attr(const char *txt, const char **atts)
+{
+ for (; *atts; atts += 2)
+ {
+ if (!strcmp(*atts, txt))
+ return atts[1];
+ }
+ return 0;
+}
+
+
+/*
+ * create evr (as Id) from 'epoch', 'version' and 'release' attributes
+ */
+
+static Id
+makeevr_atts(Pool *pool, struct parsedata *pd, const char **atts)
+{
+ const char *e, *v, *r, *v2;
+ char *c;
+ int l;
+
+ e = v = r = 0;
+ for (; *atts; atts += 2)
+ {
+ if (!strcmp(*atts, "epoch"))
+ e = atts[1];
+ else if (!strcmp(*atts, "version"))
+ v = atts[1];
+ else if (!strcmp(*atts, "release"))
+ r = atts[1];
+ }
+ if (e && !strcmp(e, "0"))
+ e = 0;
+ if (v && !e)
+ {
+ for (v2 = v; *v2 >= '0' && *v2 <= '9'; v2++)
+ ;
+ if (v2 > v && *v2 == ':')
+ e = "0";
+ }
+ l = 1;
+ if (e)
+ l += strlen(e) + 1;
+ if (v)
+ l += strlen(v);
+ if (r)
+ l += strlen(r) + 1;
+ if (l > pd->acontent)
+ {
+ pd->content = realloc(pd->content, l + 256);
+ pd->acontent = l + 256;
+ }
+ c = pd->content;
+ if (e)
+ {
+ strcpy(c, e);
+ c += strlen(c);
+ *c++ = ':';
+ }
+ if (v)
+ {
+ strcpy(c, v);
+ c += strlen(c);
+ }
+ if (r)
+ {
+ *c++ = '-';
+ strcpy(c, r);
+ c += strlen(c);
+ }
+ *c = 0;
+ if (!*pd->content)
+ return 0;
+#if 0
+ fprintf(stderr, "evr: %s\n", pd->content);
+#endif
+ return str2id(pool, pd->content, 1);
+}
+
+
+/*
+ * XML callback: startElement
+ */
+
+static void XMLCALL
+startElement(void *userData, const char *name, const char **atts)
+{
+ struct parsedata *pd = userData;
+ Pool *pool = pd->pool;
+ struct stateswitch *sw;
+ const char *str;
+
+#if 0
+ fprintf(stderr, "start: [%d]%s\n", pd->state, name);
+#endif
+ if (pd->depth != pd->statedepth)
+ {
+ pd->depth++;
+ return;
+ }
+
+ pd->depth++;
+ 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 1
+ fprintf(stderr, "into unknown: [%d]%s (from: %d)\n", sw->to, name, sw->from);
+ exit( 1 );
+#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_START:
+ break;
+ case STATE_DUINFO:
+ pd->s = pool_id2solvable(pd->pool, repo_add_solvable(pd->repo));
+ repodata_extend(pd->data, pd->s - pd->pool->solvables);
+ pd->handle = repodata_get_handle(pd->data, pd->s - pd->pool->solvables - pd->repo->start);
+ if ( (str = find_attr("name", atts)) )
+ {
+ pd->s->name = str2id(pool, str, 1);
+ }
+ pd->s->evr = makeevr_atts(pool, pd, atts);
+ if ( (str = find_attr("arch", atts)) )
+ {
+ pd->s->arch = str2id(pool, str, 1);
+ }
+ break;
+
+ case STATE_DIR:
+ {
+ long filesz = 0, filenum = 0;
+ unsigned dirid;
+ if ( (str = find_attr("name", atts)) )
+ {
+ dirid = repodata_str2dir(pd->data, str, 1);
+ }
+ else
+ {
+ fprintf( stderr, "<dir .../> tag without 'name' attribute, atts = %p, *atts = %p\n", atts, *atts);
+ break;
+ }
+ if ( (str = find_attr("size", atts)) )
+ {
+ filesz = strtol (str, 0, 0);
+ }
+ if ( (str = find_attr("count", atts)) )
+ {
+ filenum = strtol (str, 0, 0);
+ }
+ pd->dirs = sat_extend(pd->dirs, pd->ndirs, 1, sizeof(pd->dirs[0]), 31);
+ pd->dirs[pd->ndirs][0] = dirid;
+ pd->dirs[pd->ndirs][1] = filesz;
+ pd->dirs[pd->ndirs][2] = filenum;
+ pd->ndirs++;
+ }
+ break;
+ default:
+ break;
+ }
+ return;
+}
+
+
+static void XMLCALL
+endElement(void *userData, const char *name)
+{
+ struct parsedata *pd = userData;
+
+#if 0
+ fprintf(stderr, "end: %s\n", name);
+#endif
+ if (pd->depth != pd->statedepth)
+ {
+ pd->depth--;
+#if 1
+ 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_DUINFO:
+ if (pd->ndirs)
+ commit_diskusage (pd, pd->handle);
+ break;
+ default:
+ break;
+ }
+
+ pd->state = pd->sbtab[pd->state];
+ pd->docontent = 0;
+
+ return;
+}
+
+
+static void XMLCALL
+characterData(void *userData, const XML_Char *s, int len)
+{
+ struct parsedata *pd = userData;
+ int l;
+ char *c;
+ if (!pd->docontent) {
+#if 0
+ char *dup = strndup( s, len );
+ fprintf(stderr, "Content: [%d]'%s'\n", pd->state, dup );
+ free( dup );
+#endif
+ return;
+ }
+ l = pd->lcontent + len + 1;
+ if (l > pd->acontent)
+ {
+ pd->content = 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
+
+void
+repo_add_diskusagexml(Repo *repo, FILE *fp, int flags)
+{
+ Pool *pool = repo->pool;
+ struct parsedata pd;
+ char buf[BUFF_SIZE];
+ int i, l;
+ struct stateswitch *sw;
+
+ memset(&pd, 0, sizeof(pd));
+ 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;
+ }
+ pd.pool = pool;
+ pd.repo = repo;
+ pd.data = repo_add_repodata(pd.repo, 0);
+
+ pd.content = malloc(256);
+ pd.acontent = 256;
+ pd.lcontent = 0;
+ pd.tempstr = malloc(256);
+ pd.atemp = 256;
+ pd.ltemp = 0;
+ pd.datanum = 0;
+ XML_Parser 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)
+ {
+ fprintf(stderr, "repo_diskusagexml: %s at line %u:%u\n", XML_ErrorString(XML_GetErrorCode(parser)), (unsigned int)XML_GetCurrentLineNumber(parser), (unsigned int)XML_GetCurrentColumnNumber(parser));
+ exit(1);
+ }
+ if (l == 0)
+ break;
+ }
+ XML_ParserFree(parser);
+
+ if (pd.data)
+ repodata_internalize(pd.data);
+
+ free(pd.content);
+}
+
+/* EOF */