From 971d7a55de4b79b63597c8e5733d262c5d9f94af Mon Sep 17 00:00:00 2001 From: Eygene Ryabinkin Date: Tue, 26 Aug 2008 14:59:29 +0400 Subject: [PATCH 1/2] Add functions for traversing package database and matching package names These functions will help one who wants to search package database for the given package names or name globs and do something more than just collecting the names of the matched packages. They are skeleton-like functions and are supposed to be used as package database iterator and a handy match function: {{{ session = pkgwalk_begin(MATCH_STYLE); while ((pkg_name = pkgwalk_next_match(session, template))) { do something smart } pkgwalk_end(session); }}} Or a slightly more complicated version where we aren't applying the match function immediately, because matching can be slow and we will be able to use some short-curcuit checks to avoid the full matching machinery: {{{ session = pkgwalk_begin(MATCH_STYLE); while (pkgwalk_next_package(session)) { ... pkg_name = pkgwalk_get_pkgname(session); ... if (pkgwalk_matches(session, template)) { do something smart } ... } pkgwalk_end(session); }}} The new function, pkgwalk_get_pkgorigin(), was added to extract package origin during the matching session. Signed-off-by: Eygene Ryabinkin --- lib/libpkg/match.c | 260 ++++++++++++++++++++++++++++++++++++++++++++-------- lib/libpkg/pkg.h | 7 ++ 2 files changed, 230 insertions(+), 37 deletions(-) diff --git a/lib/libpkg/match.c b/lib/libpkg/match.c index ba65442..a326796 100644 --- a/lib/libpkg/match.c +++ b/lib/libpkg/match.c @@ -37,11 +37,33 @@ struct store { char **store; }; +/* + * Structure that holds information about package traversal + * and matching session. + */ +struct pkgwalk_session { + FTS *ftsp; + FTSENT *f; + match_t match_type; +}; + +/* Return values for getorigin() */ +enum st_getorigin { + e_origin_ok = 0, /* All OK */ + e_origin_emptydir, /* Package directory is empty */ + e_origin_infocorrupt, /* Package information is corrupt */ + e_origin_missing /* Package origin is not recorded */ +}; + static int rex_match(const char *, const char *, int); static int csh_match(const char *, const char *, int); struct store *storecreate(struct store *); static int storeappend(struct store *, const char *); static int fname_cmp(const FTSENT * const *, const FTSENT * const *); +static enum st_getorigin getorigin(const char *_name, char **_origin); +static inline const char * _pkgwalk_next_package(struct pkgwalk_session *_sess); +static inline int _pkgwalk_matches(struct pkgwalk_session *_sess, + char *_pattern); /* * Function to query names of installed packages. @@ -91,10 +113,10 @@ matchinstalled(match_t MatchType, char **patterns, int *retval) if (retval != NULL) *retval = 1; return NULL; - } + } } else len = 0; - + for (i = 0; i < len; i++) lmatched[i] = FALSE; @@ -107,7 +129,7 @@ matchinstalled(match_t MatchType, char **patterns, int *retval) errcode = 0; if (MatchType == MATCH_ALL) matched = f->fts_name; - else + else for (i = 0; patterns[i]; i++) { errcode = pattern_match(MatchType, patterns[i], f->fts_name); if (errcode == 1) { @@ -234,45 +256,32 @@ pattern_match(match_t MatchType, char *pattern, const char *pkgname) } /* - * Synopsis is similar to matchinstalled(), but use origin - * as a key for matching packages. + * Obtains package origin from the package name. + * Arguments: + * - name, the name of the package; + * - origin, pointer to a pointer where origin will be saved. + * On error, *origin will be NULLified. */ -char *** -matchallbyorigin(const char **origins, int *retval) +static enum st_getorigin getorigin(const char *name, char **origin) { - char **installed, **allorigins = NULL; - char ***matches = NULL; - int i, j; - - if (retval != NULL) - *retval = 0; - - installed = matchinstalled(MATCH_ALL, NULL, retval); - if (installed == NULL) - return NULL; - - /* Gather origins for all installed packages */ - for (i = 0; installed[i] != NULL; i++) { FILE *fp; - char *buf, *cp, tmp[PATH_MAX]; + char *cp, tmp[PATH_MAX]; int cmd; - allorigins = realloc(allorigins, (i + 1) * sizeof(*allorigins)); - allorigins[i] = NULL; - - snprintf(tmp, PATH_MAX, "%s/%s", LOG_DIR, installed[i]); + *origin = NULL; + snprintf(tmp, PATH_MAX, "%s/%s", LOG_DIR, name); /* * SPECIAL CASE: ignore empty dirs, since we can can see them * during port installation. */ if (isemptydir(tmp)) - continue; - strncat(tmp, "/" CONTENTS_FNAME, PATH_MAX); + return e_origin_emptydir; + if (PATH_MAX - strlen(tmp) > 0) + snprintf(tmp + strlen(tmp), PATH_MAX - strlen(tmp), + "/%s", CONTENTS_FNAME); fp = fopen(tmp, "r"); - if (fp == NULL) { - warnx("the package info for package '%s' is corrupt", installed[i]); - continue; - } + if (fp == NULL) + return e_origin_infocorrupt; cmd = -1; while (fgets(tmp, sizeof(tmp), fp)) { @@ -287,14 +296,53 @@ matchallbyorigin(const char **origins, int *retval) continue; cmd = plist_cmd(tmp + 1, &cp); if (cmd == PLIST_ORIGIN) { - asprintf(&buf, "%s", cp); - allorigins[i] = buf; + asprintf(origin, "%s", cp); break; } } - if (cmd != PLIST_ORIGIN && 0 != strncmp("bsdpan-", installed[i], 7)) - warnx("package %s has no origin recorded", installed[i]); fclose(fp); + if (cmd != PLIST_ORIGIN) + return e_origin_missing; + else + return e_origin_ok; +} + +/* + * Synopsis is similar to matchinstalled(), but use origin + * as a key for matching packages. + */ +char *** +matchallbyorigin(const char **origins, int *retval) +{ + char **installed, **allorigins = NULL; + char ***matches = NULL; + int i, j; + + if (retval != NULL) + *retval = 0; + + installed = matchinstalled(MATCH_ALL, NULL, retval); + if (installed == NULL) + return NULL; + + /* Gather origins for all installed packages */ + for (i = 0; installed[i] != NULL; i++) { + allorigins = realloc(allorigins, (i + 1) * sizeof(*allorigins)); + allorigins[i] = NULL; + + switch (getorigin(installed[i], &allorigins[i])) { + case e_origin_ok: + break; + case e_origin_emptydir: + continue; + case e_origin_infocorrupt: + warnx("the package info for package '%s' is corrupt", installed[i]); + continue; + case e_origin_missing: + if (Verbose || 0 != strncmp("bsdpan-", installed[i], 7)) + warnx("package %s has no origin recorded", installed[i]); + break; + } } /* Resolve origins into package names, retaining the sequence */ @@ -359,7 +407,7 @@ struct iip_memo { LIST_HEAD(, iip_memo) iip_memo = LIST_HEAD_INITIALIZER(iip_memo); /* - * + * * Return 1 if the specified package is installed, * 0 if not, and -1 if an error occured. */ @@ -374,7 +422,7 @@ isinstalledpkg(const char *name) if (strcmp(memo->iip_name, name) == 0) return memo->iip_result; } - + buf2 = NULL; asprintf(&buf, "%s/%s", LOG_DIR, name); if (buf == NULL) @@ -416,6 +464,144 @@ errout: } /* + * Prepares package database for the traversal. + */ +struct pkgwalk_session * +pkgwalk_begin(match_t MatchType) +{ + struct pkgwalk_session *sess; + const char *paths[2] = {LOG_DIR, NULL}; + + if (!isdir(paths[0])) + return NULL; + + sess = (struct pkgwalk_session *)malloc(sizeof(*sess)); + if (sess == NULL) + return NULL; + bzero((void *)sess, sizeof(*sess)); + + sess->match_type = MatchType; + sess->ftsp = fts_open((char * const *)(uintptr_t)paths, + FTS_LOGICAL | FTS_NOCHDIR | FTS_NOSTAT, fname_cmp); + if (sess->ftsp == NULL) { + free((void *)sess); + return NULL; + } + + return sess; +} + +/* + * Ends package traversal session. + */ +void +pkgwalk_end(struct pkgwalk_session *sess) +{ + if (sess == NULL) + return; + if (sess->ftsp != NULL) + fts_close(sess->ftsp); + bzero((void *)sess, sizeof(*sess)); + free((void *)sess); + return; +} + +/* + * Proceeds to the next package in the session. + * Returns package name if package was selected + * or NULL otherwise. + */ +const char * +pkgwalk_next_package(struct pkgwalk_session *sess) +{ + if (sess == NULL || sess->ftsp == NULL) + return NULL; + return _pkgwalk_next_package(sess); +} +static inline const char * +_pkgwalk_next_package(struct pkgwalk_session *sess) +{ + while ((sess->f = fts_read(sess->ftsp)) != NULL) { + if (sess->f->fts_info == FTS_D && + sess->f->fts_level == 1) { + fts_set(sess->ftsp, sess->f, FTS_SKIP); + return (const char *)(sess->f->fts_name); + } + } + return NULL; +} + +/* + * Matches the current package name against the given pattern. + * Returns 1 if pattern matches, 0 if not matches and -1 for + * the error condition. + */ +int +pkgwalk_matches(struct pkgwalk_session *sess, char *pattern) +{ + if (sess == NULL || sess->ftsp == NULL || sess->f == NULL || + pattern == NULL) + return FALSE; + + return _pkgwalk_matches(sess, pattern); +} +static inline int +_pkgwalk_matches(struct pkgwalk_session *sess, char *pattern) +{ + return pattern_match(sess->match_type, pattern, + sess->f->fts_name); +} + +/* + * Proceeds to the next package in the session that matches + * the given pattern. + * Returns the name of the selected package or NULL otherwise. + */ +const char * +pkgwalk_next_match(struct pkgwalk_session *sess, char *pattern) +{ + const char *retval = NULL; + + if (sess == NULL || sess->ftsp == NULL || sess->f == NULL || + pattern == NULL) + return NULL; + + while ((retval = _pkgwalk_next_package(sess)) != NULL) { + if (_pkgwalk_matches(sess, pattern)) + break; + } + return retval; +} + +/* + * Returns name of the current package. + */ +const char * +pkgwalk_get_pkgname(struct pkgwalk_session *sess) +{ + if (sess == NULL || sess->ftsp == NULL || sess->f == NULL) + return NULL; + + return (const char *)(sess->f->fts_name); +} + +/* + * Returns origin of the current package. Caller should free the + * returned value when it will not be needed anymore. + */ +const char * +pkgwalk_get_pkgorigin(struct pkgwalk_session *sess) +{ + char *origin; + + if (sess == NULL || sess->ftsp == NULL || sess->f == NULL) + return NULL; + + getorigin(sess->f->fts_name, &origin); + return (const char *)origin; +} + +/* * Returns 1 if specified pkgname matches RE pattern. * Otherwise returns 0 if doesn't match or -1 if RE * engine reported an error (usually invalid syntax). diff --git a/lib/libpkg/pkg.h b/lib/libpkg/pkg.h index 12e3562..fe590a7 100644 --- a/lib/libpkg/pkg.h +++ b/lib/libpkg/pkg.h @@ -225,6 +225,13 @@ char **matchbyorigin(const char *, int *); char ***matchallbyorigin(const char **, int *); int isinstalledpkg(const char *name); int pattern_match(match_t MatchType, char *pattern, const char *pkgname); +struct pkgwalk_session *pkgwalk_begin(match_t); +void pkgwalk_end(struct pkgwalk_session *); +const char *pkgwalk_next_package(struct pkgwalk_session *); +int pkgwalk_matches(struct pkgwalk_session *, char *); +const char * pkgwalk_next_match(struct pkgwalk_session *, char *); +const char *pkgwalk_get_pkgname(struct pkgwalk_session *); +const char *pkgwalk_get_pkgorigin(struct pkgwalk_session *); /* Dependencies */ int sortdeps(char **); -- 1.7.3.2