Hello,

I am thinking an iterator-like interface for database reading could be
useful. The most obvious example use is the pkgfile functionality.
It could be a convenient tool to do one-pass work on databases
without loading them completely in memory, which can be impossible for
databases with files info (I don't think everyone can load the whole
[community] database using libalpm standard structures).

Here's a patch that roughly shows what I am thinking about: only the code
for the "local" backend is shown.

diff --git a/lib/libalpm/alpm.h b/lib/libalpm/alpm.h
index 6e1e4bc..986e9a1 100644
--- a/lib/libalpm/alpm.h
+++ b/lib/libalpm/alpm.h
@@ -119,6 +119,7 @@ typedef enum _alpm_sigstatus_t {
 
 typedef struct __alpm_handle_t alpm_handle_t;
 typedef struct __alpm_db_t alpm_db_t;
+typedef struct __alpm_dbreader_t alpm_dbreader_t;
 typedef struct __alpm_pkg_t alpm_pkg_t;
 typedef struct __alpm_trans_t alpm_trans_t;
 
@@ -371,6 +372,9 @@ int alpm_option_set_default_siglevel(alpm_handle_t *handle, 
alpm_siglevel_t leve
  * @{
  */
 
+alpm_dbreader_t *alpm_open_dbdir(alpm_handle_t *handle, const char *dbpath);
+alpm_dbreader_t *alpm_open_dbarchive(alpm_handle_t *handle, const char 
*dbpath);
+
 /** Get the database of locally installed packages.
  * The returned pointer points to an internal structure
  * of libalpm which should only be manipulated through
diff --git a/lib/libalpm/be_local.c b/lib/libalpm/be_local.c
index 70f242d..74d6886 100644
--- a/lib/libalpm/be_local.c
+++ b/lib/libalpm/be_local.c
@@ -41,15 +41,22 @@
 #include "package.h"
 #include "deps.h"
 
-static int local_db_read(alpm_pkg_t *info, alpm_dbinfrq_t inforeq);
+static int local_db_read(const char *path, alpm_pkg_t *info, alpm_dbinfrq_t 
inforeq);
 
 #define LAZY_LOAD(info, errret) \
        do { \
                if(!(pkg->infolevel & info)) { \
-                       local_db_read(pkg, info); \
+                       local_db_read(NULL, pkg, info); \
                } \
        } while(0)
 
+struct local_dbreader_t {
+       alpm_handle_t *handle;
+       alpm_pkg_t *(*getitem) (alpm_dbreader_t*);
+       DIR* dbdir;
+       const char *dbpath;
+       char finished;
+};
 
 /* Cache-specific accessor functions. These implementations allow for lazy
  * loading by the files backend when a data member is actually needed
@@ -235,7 +242,7 @@ static int _cache_changelog_close(const alpm_pkg_t UNUSED 
*pkg, void *fp)
 
 static int _cache_force_load(alpm_pkg_t *pkg)
 {
-       return local_db_read(pkg, INFRQ_ALL);
+       return local_db_read(NULL, pkg, INFRQ_ALL);
 }
 
 
@@ -376,9 +383,10 @@ static int local_db_populate(alpm_db_t *db)
        size_t est_count;
        int count = 0;
        struct stat buf;
-       struct dirent *ent = NULL;
        const char *dbpath;
        DIR *dbdir;
+       alpm_dbreader_t *reader;
+       alpm_pkg_t *pkg;
 
        dbpath = _alpm_db_path(db);
        if(dbpath == NULL) {
@@ -408,7 +416,6 @@ static int local_db_populate(alpm_db_t *db)
                while(readdir(dbdir) != NULL) {
                        est_count++;
                }
-               rewinddir(dbdir);
        }
        if(est_count >= 2) {
                /* subtract the two extra pointers to get # of children */
@@ -418,35 +425,13 @@ static int local_db_populate(alpm_db_t *db)
        /* initialize hash at 50% full */
        db->pkgcache = _alpm_pkghash_create(est_count * 2);
        if(db->pkgcache == NULL){
-               closedir(dbdir);
                RET_ERR(db->handle, ALPM_ERR_MEMORY, -1);
        }
+       closedir(dbdir);
 
-       while((ent = readdir(dbdir)) != NULL) {
-               const char *name = ent->d_name;
-
-               alpm_pkg_t *pkg;
-
-               if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
-                       continue;
-               }
-               if(!is_dir(dbpath, ent)) {
-                       continue;
-               }
-
-               pkg = _alpm_pkg_new();
-               if(pkg == NULL) {
-                       closedir(dbdir);
-                       RET_ERR(db->handle, ALPM_ERR_MEMORY, -1);
-               }
-               /* split the db entry name */
-               if(_alpm_splitname(name, &(pkg->name), &(pkg->version),
-                                       &(pkg->name_hash)) != 0) {
-                       _alpm_log(db->handle, ALPM_LOG_ERROR, _("invalid name 
for database entry '%s'\n"),
-                                       name);
-                       _alpm_pkg_free(pkg);
-                       continue;
-               }
+       reader = alpm_open_dbdir(db->handle, dbpath);
+       while((pkg = reader->getitem(reader))) {
+               pkg->origin_data.db = db;
 
                /* duplicated database entries are not allowed */
                if(_alpm_pkghash_find(db->pkgcache, pkg->name)) {
@@ -455,18 +440,6 @@ static int local_db_populate(alpm_db_t *db)
                        continue;
                }
 
-               pkg->origin = PKG_FROM_LOCALDB;
-               pkg->origin_data.db = db;
-               pkg->ops = &local_pkg_ops;
-               pkg->handle = db->handle;
-
-               /* explicitly read with only 'BASE' data, accessors will handle 
the rest */
-               if(local_db_read(pkg, INFRQ_BASE) == -1) {
-                       _alpm_log(db->handle, ALPM_LOG_ERROR, _("corrupted 
database entry '%s'\n"), name);
-                       _alpm_pkg_free(pkg);
-                       continue;
-               }
-
                /* add to the collection */
                _alpm_log(db->handle, ALPM_LOG_FUNCTION, "adding '%s' to 
package cache for db '%s'\n",
                                pkg->name, db->treename);
@@ -474,7 +447,6 @@ static int local_db_populate(alpm_db_t *db)
                count++;
        }
 
-       closedir(dbdir);
        if(count > 0) {
                db->pkgcache->list = alpm_list_msort(db->pkgcache->list, 
(size_t)count, _alpm_pkg_cmp);
        }
@@ -516,13 +488,13 @@ static char *get_pkgpath(alpm_db_t *db, alpm_pkg_t *info)
        f = alpm_list_add(f, linedup); \
 } while(1) /* note the while(1) and not (0) */
 
-static int local_db_read(alpm_pkg_t *info, alpm_dbinfrq_t inforeq)
+static int local_db_read(const char* pkgpath, alpm_pkg_t *info, alpm_dbinfrq_t 
inforeq)
 {
        FILE *fp = NULL;
        char path[PATH_MAX];
        char line[1024];
-       char *pkgpath = NULL;
-       alpm_db_t *db = info->origin_data.db;
+  alpm_db_t *db = info->origin_data.db;
+       const char *treename = db ? db->treename : "NULL";
 
        /* bitmask logic here:
         * infolevel: 00001111
@@ -533,18 +505,20 @@ static int local_db_read(alpm_pkg_t *info, alpm_dbinfrq_t 
inforeq)
                /* already loaded all of this info, do nothing */
                return 0;
        }
-       _alpm_log(db->handle, ALPM_LOG_FUNCTION, "loading package data for %s : 
level=0x%x\n",
+       _alpm_log(info->handle, ALPM_LOG_FUNCTION, "loading package data for %s 
: level=0x%x\n",
                        info->name, inforeq);
 
        /* clear out 'line', to be certain - and to make valgrind happy */
        memset(line, 0, sizeof(line));
 
-       pkgpath = get_pkgpath(db, info);
+       if (pkgpath == NULL) {
+               pkgpath = get_pkgpath(db, info);
+       }
 
        if(access(pkgpath, F_OK)) {
                /* directory doesn't exist or can't be opened */
-               _alpm_log(db->handle, ALPM_LOG_DEBUG, "cannot find '%s-%s' in 
db '%s'\n",
-                               info->name, info->version, db->treename);
+               _alpm_log(info->handle, ALPM_LOG_DEBUG, "cannot find '%s-%s' in 
db '%s'\n",
+                               info->name, info->version, db ? treename : 
"NULL");
                goto error;
        }
 
@@ -552,7 +526,7 @@ static int local_db_read(alpm_pkg_t *info, alpm_dbinfrq_t 
inforeq)
        if(inforeq & INFRQ_DESC && !(info->infolevel & INFRQ_DESC)) {
                snprintf(path, PATH_MAX, "%sdesc", pkgpath);
                if((fp = fopen(path, "r")) == NULL) {
-                       _alpm_log(db->handle, ALPM_LOG_ERROR, _("could not open 
file %s: %s\n"), path, strerror(errno));
+                       _alpm_log(info->handle, ALPM_LOG_ERROR, _("could not 
open file %s: %s\n"), path, strerror(errno));
                        goto error;
                }
                while(!feof(fp)) {
@@ -560,14 +534,14 @@ static int local_db_read(alpm_pkg_t *info, alpm_dbinfrq_t 
inforeq)
                        if(strcmp(line, "%NAME%") == 0) {
                                READ_NEXT();
                                if(strcmp(line, info->name) != 0) {
-                                       _alpm_log(db->handle, ALPM_LOG_ERROR, 
_("%s database is inconsistent: name "
-                                                               "mismatch on 
package %s\n"), db->treename, info->name);
+                                       _alpm_log(info->handle, ALPM_LOG_ERROR, 
_("%s database is inconsistent: name "
+                                                               "mismatch on 
package %s\n"), treename, info->name);
                                }
                        } else if(strcmp(line, "%VERSION%") == 0) {
                                READ_NEXT();
                                if(strcmp(line, info->version) != 0) {
-                                       _alpm_log(db->handle, ALPM_LOG_ERROR, 
_("%s database is inconsistent: version "
-                                                               "mismatch on 
package %s\n"), db->treename, info->name);
+                                       _alpm_log(info->handle, ALPM_LOG_ERROR, 
_("%s database is inconsistent: version "
+                                                               "mismatch on 
package %s\n"), treename, info->name);
                                }
                        } else if(strcmp(line, "%DESC%") == 0) {
                                READ_AND_STORE(info->desc);
@@ -625,7 +599,7 @@ static int local_db_read(alpm_pkg_t *info, alpm_dbinfrq_t 
inforeq)
        if(inforeq & INFRQ_FILES && !(info->infolevel & INFRQ_FILES)) {
                snprintf(path, PATH_MAX, "%sfiles", pkgpath);
                if((fp = fopen(path, "r")) == NULL) {
-                       _alpm_log(db->handle, ALPM_LOG_ERROR, _("could not open 
file %s: %s\n"), path, strerror(errno));
+                       _alpm_log(info->handle, ALPM_LOG_ERROR, _("could not 
open file %s: %s\n"), path, strerror(errno));
                        goto error;
                }
                while(fgets(line, sizeof(line), fp)) {
@@ -664,11 +638,9 @@ static int local_db_read(alpm_pkg_t *info, alpm_dbinfrq_t 
inforeq)
        /* internal */
        info->infolevel |= inforeq;
 
-       free(pkgpath);
        return 0;
 
 error:
-       free(pkgpath);
        if(fp) {
                fclose(fp);
        }
@@ -913,4 +885,96 @@ alpm_db_t *_alpm_db_register_local(alpm_handle_t *handle)
        return db;
 }
 
+static alpm_pkg_t *local_dbreader_getitem(struct local_dbreader_t *reader) {
+       struct dirent *ent = NULL;
+       reader->handle->pm_errno = 0;
+       if (reader->finished) {
+               /* iteration has already finished */
+               RET_ERR(reader->handle, ALPM_ERR_WRONG_ARGS, NULL);
+       }
+       if (reader->dbdir == NULL) {
+               reader->finished = 1;
+               return NULL;
+       }
+
+       while(1) {
+               ent = readdir(reader->dbdir);
+               if (ent == NULL) {
+                       closedir(reader->dbdir);
+                       reader->finished = 1;
+                       return NULL;
+               }
+               const char *name = ent->d_name;
+               char fullpath[PATH_MAX];
+               snprintf(fullpath, PATH_MAX, "%s/%s", reader->dbpath, name);
+
+               alpm_pkg_t *pkg;
+
+               if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
+                       continue;
+               }
+               if(!is_dir(reader->dbpath, ent)) {
+                       continue;
+               }
+
+               pkg = _alpm_pkg_new();
+               if(pkg == NULL) {
+                       closedir(reader->dbdir);
+                       reader->finished = 1;
+                       RET_ERR(reader->handle, ALPM_ERR_MEMORY, NULL);
+               }
+               pkg->handle = reader->handle;
+               /* split the db entry name */
+               if(_alpm_splitname(name, &(pkg->name), &(pkg->version),
+                                       &(pkg->name_hash)) != 0) {
+                       _alpm_log(reader->handle, ALPM_LOG_ERROR, _("invalid 
name for database entry '%s'\n"),
+                                       name);
+                       _alpm_pkg_free(pkg);
+                       continue;
+               }
+
+               pkg->origin = PKG_FROM_LOCALDB;
+               pkg->origin_data.db = NULL;
+               pkg->ops = &local_pkg_ops;
+
+               /* explicitly read with only 'BASE' data, accessors will handle 
the rest */
+               if(local_db_read(fullpath, pkg, INFRQ_BASE) == -1) {
+                       _alpm_log(reader->handle, ALPM_LOG_ERROR, _("corrupted 
database entry '%s'\n"), name);
+                       _alpm_pkg_free(pkg);
+                       continue;
+               }
+               return pkg;
+       }
+}
+
+alpm_dbreader_t SYMEXPORT *alpm_open_dbdir(alpm_handle_t *handle, const char 
*dbpath) {
+       struct local_dbreader_t *reader = malloc(sizeof(struct 
local_dbreader_t));
+       DIR *dbdir;
+       if(handle == NULL) {
+               return NULL;
+       }
+       if(reader == NULL) {
+               RET_ERR(handle, ALPM_ERR_MEMORY, NULL);
+       }
+       if(dbpath == NULL) {
+               RET_ERR(handle, ALPM_ERR_WRONG_ARGS, NULL);
+       }
+
+       /* start reading */
+       dbdir = opendir(dbpath);
+       if(dbdir == NULL) {
+               if(errno != ENOENT) {
+                       /* no database existing yet is not an error */
+                       free(reader);
+                       return NULL;
+               }
+       }
+       reader->getitem = &local_dbreader_getitem;
+       reader->handle = handle;
+       reader->dbdir = dbdir;
+       reader->dbpath = dbpath;
+       reader->finished = 0;
+       return (alpm_dbreader_t*)reader;
+}
+
 /* vim: set ts=2 sw=2 noet: */
diff --git a/lib/libalpm/db.h b/lib/libalpm/db.h
index a950130..3ce5889 100644
--- a/lib/libalpm/db.h
+++ b/lib/libalpm/db.h
@@ -56,6 +56,11 @@ struct db_operations {
        void (*unregister) (alpm_db_t *);
 };
 
+struct __alpm_dbreader_t {
+       alpm_handle_t *handle;
+       alpm_pkg_t *(*getitem) (alpm_dbreader_t*);
+};
+
 /* Database */
 struct __alpm_db_t {
        alpm_handle_t *handle;

-- 
Rémy.

Reply via email to