diff --git a/NEWS b/NEWS index 3e7c9df9..e0905ba3 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,11 @@ xbps-0.8.0 (???): + * Merged the `progress_callback' branch that makes possible to specify a + function callback to update progress while unpacking binary packages + and while fetching files. xbps-bin(8) now reports a percentage while + unpacking package files if -v not set, otherwise it will list the + current extracted file with its size in bytes. + * Improved package dependency resolution in repositories by using a new function, xbps_repository_pool_find_pkg() that returns a package dictionary found in the first repository registered in the pool. It can also be used diff --git a/bin/Makefile b/bin/Makefile index f7f50693..3d578483 100644 --- a/bin/Makefile +++ b/bin/Makefile @@ -1,9 +1,9 @@ -include ../config.mk -SUBDIRS = xbps-uhelper +SUBDIRS = xbps-bin SUBDIRS += xbps-repo -SUBDIRS += xbps-bin SUBDIRS += xbps-dgraph +SUBDIRS += xbps-uhelper .PHONY: all all: diff --git a/bin/xbps-bin/Makefile b/bin/xbps-bin/Makefile index 3a72fbf5..3a34d986 100644 --- a/bin/xbps-bin/Makefile +++ b/bin/xbps-bin/Makefile @@ -3,8 +3,8 @@ TOPDIR = ../.. BIN = xbps-bin OBJS = check.o install.o main.o remove.o show-deps.o -OBJS += show-info-files.o ../xbps-repo/util.o find-files.o -OBJS += question.o +OBJS += show-info-files.o util.o find-files.o +OBJS += question.o fetch.o MAN = $(BIN).8 include $(TOPDIR)/prog.mk diff --git a/bin/xbps-bin/defs.h b/bin/xbps-bin/defs.h index d65f6edc..42ca750f 100644 --- a/bin/xbps-bin/defs.h +++ b/bin/xbps-bin/defs.h @@ -30,6 +30,8 @@ #define __UNCONST(a) ((void *)(unsigned long)(const void *)(a)) #endif +#include + int xbps_install_new_pkg(const char *); int xbps_update_pkg(const char *); int xbps_autoupdate_pkgs(bool); @@ -43,7 +45,22 @@ int xbps_show_pkg_reverse_deps(const char *); int show_pkg_info_from_metadir(const char *); int show_pkg_files_from_metadir(const char *); int find_files_in_packages(const char *); + +/* from question.c */ bool xbps_yesno(const char *, ...); bool xbps_noyes(const char *, ...); +/* from fetch.c */ +void fetch_file_progress_cb(void *); + +/* From util.c */ +int show_pkg_files(prop_dictionary_t); +void show_pkg_info(prop_dictionary_t); +void show_pkg_info_only_repo(prop_dictionary_t); +int show_pkg_namedesc(prop_object_t, void *, bool *); +int list_strings_in_array(prop_object_t, void *, bool *); +int list_strings_sep_in_array(prop_object_t, void *, bool *); +size_t find_longest_pkgver(prop_dictionary_t); +void print_package_line(const char *); + #endif /* !_XBPS_BIN_DEFS_H_ */ diff --git a/bin/xbps-bin/fetch.c b/bin/xbps-bin/fetch.c new file mode 100644 index 00000000..03d2afdd --- /dev/null +++ b/bin/xbps-bin/fetch.c @@ -0,0 +1,187 @@ +/*- + * Copyright (c) 2009-2011 Juan Romero Pardines + * Copyright (c) 2000-2004 Dag-Erling Coïdan Smørgrav + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer + * in this position and unchanged. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * From FreeBSD fetch(8): + * $FreeBSD: src/usr.bin/fetch/fetch.c,v 1.84.2.1 2009/08/03 08:13:06 kensmith Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "defs.h" + +struct xferstat { + struct timeval start; + struct timeval last; +}; + +/* + * Compute and display ETA + */ +static const char * +stat_eta(struct xbps_fetch_progress_data *xfpd, struct xferstat *xsp) +{ + static char str[16]; + long elapsed, eta; + off_t received, expected; + + elapsed = xsp->last.tv_sec - xsp->start.tv_sec; + received = xfpd->file_dloaded - xfpd->file_offset; + expected = xfpd->file_size - xfpd->file_dloaded; + eta = (long)(elapsed * expected / received); + if (eta > 3600) + snprintf(str, sizeof str, "%02ldh%02ldm", + eta / 3600, (eta % 3600) / 60); + else + snprintf(str, sizeof str, "%02ldm%02lds", + eta / 60, eta % 60); + + return str; +} + +/** High precision double comparison + * \param[in] a The first double to compare + * \param[in] b The second double to compare + * \return true on equal within precison, false if not equal within defined precision. + */ +static inline bool +compare_double(const double a, const double b) +{ + const double precision = 0.00001; + + if ((a - precision) < b && (a + precision) > b) + return true; + else + return false; +} + +/* + * Compute and display transfer rate + */ +static const char * +stat_bps(struct xbps_fetch_progress_data *xfpd, struct xferstat *xsp) +{ + static char str[16]; + char size[8]; + double delta, bps; + + delta = (xsp->last.tv_sec + (xsp->last.tv_usec / 1.e6)) + - (xsp->start.tv_sec + (xsp->start.tv_usec / 1.e6)); + if (compare_double(delta, 0.0001)) { + snprintf(str, sizeof str, "-- stalled --"); + } else { + bps = + ((double)(xfpd->file_dloaded - xfpd->file_offset) / delta); + (void)xbps_humanize_number(size, (int64_t)bps); + snprintf(str, sizeof str, "%s/s", size); + } + return str; +} + +/* + * Update the stats display + */ +static void +stat_display(struct xbps_fetch_progress_data *xfpd, struct xferstat *xsp) +{ + struct timeval now; + char totsize[8], recvsize[8]; + + gettimeofday(&now, NULL); + if (now.tv_sec <= xsp->last.tv_sec) + return; + xsp->last = now; + + (void)xbps_humanize_number(totsize, (int64_t)xfpd->file_size); + (void)xbps_humanize_number(recvsize, (int64_t)xfpd->file_dloaded); + fprintf(stderr,"\r%s: %s [%d%% of %s]", xfpd->file_name, recvsize, + (int)((double)(100.0 * + (double)xfpd->file_dloaded) / (double)xfpd->file_size), totsize); + fprintf(stderr," %s", stat_bps(xfpd, xsp)); + if (xfpd->file_size > 0 && xfpd->file_dloaded > 0 && + xsp->last.tv_sec >= xsp->start.tv_sec + 10) + fprintf(stderr," ETA: %s", stat_eta(xfpd, xsp)); + + fprintf(stderr,"\033[K"); +} + +/* + * Initialize the transfer statistics + */ +static void +stat_start(struct xferstat *xsp) +{ + gettimeofday(&xsp->start, NULL); + xsp->last.tv_sec = xsp->last.tv_usec = 0; +} + +/* + * Update the transfer statistics + */ +static void +stat_update(struct xbps_fetch_progress_data *xfpd, struct xferstat *xsp) +{ + xfpd->file_dloaded += xfpd->file_offset; + stat_display(xfpd, xsp); +} + +/* + * Finalize the transfer statistics + */ +static void +stat_end(struct xbps_fetch_progress_data *xfpd, struct xferstat *xsp) +{ + char size[8]; + + (void)xbps_humanize_number(size, (int64_t)xfpd->file_size); + fprintf(stderr,"\rDownloaded %s for %s [avg rate: %s]", + size, xfpd->file_name, stat_bps(xfpd, xsp)); + fprintf(stderr,"\033[K\n"); +} + +void +fetch_file_progress_cb(void *data) +{ + struct xbps_fetch_progress_data *xfpd = data; + static struct xferstat xs; + + if (xfpd->cb_start) + stat_start(&xs); + else if (xfpd->cb_update) + stat_update(xfpd, &xs); + else if (xfpd->cb_end) + stat_end(xfpd, &xs); +} diff --git a/bin/xbps-bin/install.c b/bin/xbps-bin/install.c index fc73a455..8018ef4c 100644 --- a/bin/xbps-bin/install.c +++ b/bin/xbps-bin/install.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2009-2010 Juan Romero Pardines. + * Copyright (c) 2009-2011 Juan Romero Pardines. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,8 +31,10 @@ #include #include #include +#include #include +#include "strlcpy.h" #include "defs.h" #include "../xbps-repo/defs.h" @@ -98,6 +100,7 @@ static int download_package_list(prop_object_iterator_t iter) { prop_object_t obj; + struct xbps_fetch_progress_data xfpd; const char *pkgver, *repoloc, *filename, *cachedir, *sha256; char *binfile; int rv = 0; @@ -143,7 +146,8 @@ again: return errno; } printf("Downloading %s binary package ...\n", pkgver); - rv = xbps_fetch_file(binfile, cachedir, false, NULL); + rv = xbps_fetch_file(binfile, cachedir, false, NULL, + fetch_file_progress_cb, &xfpd); if (rv == -1) { fprintf(stderr, "xbps-bin: couldn't download `%s'\n", filename); @@ -421,14 +425,48 @@ replace_packages(prop_dictionary_t trans_dict, prop_dictionary_t pkgd, return 0; } +static void +unpack_progress_cb_verbose(void *data) +{ + struct xbps_unpack_progress_data *xpd = data; + + if (xpd->entry == NULL || xpd->entry_is_metadata) + return; + else if (xpd->entry_size <= 0) + return; + + fprintf(stderr, "Extracted %sfile `%s' (%" PRIi64 " bytes)\n", + xpd->entry_is_conf ? "configuration " : "", xpd->entry, + xpd->entry_size); +} + +static void +unpack_progress_cb_percentage(void *data) +{ + struct xbps_unpack_progress_data *xpd = data; + double percent = 0; + + if (xpd->entry_is_metadata) + return; + + percent = + (double)((xpd->entry_extract_count * 100.0) / xpd->entry_total_count); + if (percent > 100.0 || + xpd->entry_extract_count >= xpd->entry_total_count) + percent = 100.0; + + printf("\033[s(%3.2f%%)\033[u", percent); +} + static int exec_transaction(struct transaction *trans) { prop_dictionary_t instpkgd; prop_object_t obj; prop_object_iterator_t replaces_iter; - const char *pkgname, *version, *pkgver, *instver, *filename, *tract; - int rv = 0; + struct xbps_unpack_progress_data xpd; + const char *pkgname, *version, *pkgver, *instver, *filen, *tract; + int flags = xbps_get_flags(), rv = 0; bool update, preserve, autoinst; pkg_state_t state = 0; @@ -470,13 +508,13 @@ exec_transaction(struct transaction *trans) prop_dictionary_get_cstring_nocopy(obj, "pkgname", &pkgname); prop_dictionary_get_cstring_nocopy(obj, "version", &version); prop_dictionary_get_cstring_nocopy(obj, "pkgver", &pkgver); - prop_dictionary_get_cstring_nocopy(obj, "filename", &filename); + prop_dictionary_get_cstring_nocopy(obj, "filename", &filen); prop_dictionary_get_cstring_nocopy(obj, "trans-action", &tract); assert(pkgname != NULL); assert(version != NULL); assert(pkgver != NULL); - assert(filename != NULL); + assert(filen != NULL); assert(tract != NULL); prop_dictionary_get_bool(obj, "automatic-install", &autoinst); @@ -536,12 +574,24 @@ exec_transaction(struct transaction *trans) /* * Unpack binary package. */ - printf("Unpacking %s (from .../%s) ...\n", pkgver, filename); - if ((rv = xbps_unpack_binary_pkg(obj)) != 0) { + printf("Unpacking `%s' (from ../%s) ... ", pkgver, filen); + + if (flags & XBPS_FLAG_VERBOSE) { + rv = xbps_unpack_binary_pkg(obj, + unpack_progress_cb_verbose, &xpd); + printf("\n"); + } else { + rv = xbps_unpack_binary_pkg(obj, + unpack_progress_cb_percentage, &xpd); + } + if (rv != 0) { fprintf(stderr, "xbps-bin: error unpacking %s " "(%s)\n", pkgver, strerror(rv)); return rv; } + if ((flags & XBPS_FLAG_VERBOSE) == 0) + printf("\n"); + /* * Register binary package. */ diff --git a/bin/xbps-bin/remove.c b/bin/xbps-bin/remove.c index 555dab67..8e6330c0 100644 --- a/bin/xbps-bin/remove.c +++ b/bin/xbps-bin/remove.c @@ -41,7 +41,7 @@ pkg_remove_and_purge(const char *pkgname, const char *version, bool purge) printf("Removing package %s-%s ...\n", pkgname, version); if ((rv = xbps_remove_pkg(pkgname, version, false)) != 0) { - fprintf(stderr, "\nE: unable to remove %s-%s (%s).\n", + xbps_error_printf("unable to remove %s-%s (%s).\n", pkgname, version, strerror(errno)); return rv; } @@ -49,7 +49,7 @@ pkg_remove_and_purge(const char *pkgname, const char *version, bool purge) printf(" Purging ... "); (void)fflush(stdout); if ((rv = xbps_purge_pkg(pkgname, false)) != 0) { - fprintf(stderr, "\nE: unable to purge %s-%s " + xbps_error_printf("unable to purge %s-%s " "(%s).\n", pkgname, version, strerror(errno)); return rv; @@ -149,7 +149,7 @@ xbps_remove_installed_pkgs(int argc, char **argv, bool yes, bool purge, found = true; reqby = prop_dictionary_get(dict, "requiredby"); if (reqby != NULL && prop_array_count(reqby) > 0) { - printf("WARNING: %s IS REQUIRED BY %u PACKAGES!\n", + xbps_warn_printf("%s IS REQUIRED BY %u PACKAGES!\n", pkgver, prop_array_count(reqby)); reqby_force = true; } @@ -184,7 +184,7 @@ xbps_remove_installed_pkgs(int argc, char **argv, bool yes, bool purge, prop_object_release(sorted_pkgs); return 0; } else if (reqby_force && force_rm_with_deps) - printf("WARNING: Forcing removal! you've been alerted.\n"); + xbps_warn_printf("Forcing removal! you've been alerted.\n"); for (x = 0; x < prop_array_count(sorted_pkgs); x++) { dict = prop_array_get(sorted_pkgs, x); diff --git a/bin/xbps-repo/util.c b/bin/xbps-bin/util.c similarity index 99% rename from bin/xbps-repo/util.c rename to bin/xbps-bin/util.c index e9c9603b..b6a19e28 100644 --- a/bin/xbps-repo/util.c +++ b/bin/xbps-bin/util.c @@ -34,6 +34,7 @@ #include #include "strlcpy.h" #include "defs.h" +#include "../xbps-repo/defs.h" void show_pkg_info_only_repo(prop_dictionary_t dict) diff --git a/bin/xbps-repo/Makefile b/bin/xbps-repo/Makefile index 59fdb3f6..c0716a27 100644 --- a/bin/xbps-repo/Makefile +++ b/bin/xbps-repo/Makefile @@ -2,7 +2,8 @@ TOPDIR = ../.. -include $(TOPDIR)/config.mk BIN = xbps-repo -OBJS = main.o util.o index.o repository.o find-files.o +OBJS = main.o index.o repository.o find-files.o +OBJS += ../xbps-bin/fetch.o ../xbps-bin/util.o MAN = $(BIN).8 include $(TOPDIR)/prog.mk diff --git a/bin/xbps-repo/defs.h b/bin/xbps-repo/defs.h index 1d67005c..38e2fdb1 100644 --- a/bin/xbps-repo/defs.h +++ b/bin/xbps-repo/defs.h @@ -38,15 +38,6 @@ int unregister_repository(const char *); int show_pkg_info_from_repolist(const char *); int show_pkg_deps_from_repolist(const char *); int repository_sync(void); -/* From util.c */ -int show_pkg_files(prop_dictionary_t); -void show_pkg_info(prop_dictionary_t); -void show_pkg_info_only_repo(prop_dictionary_t); -int show_pkg_namedesc(prop_object_t, void *, bool *); -int list_strings_in_array(prop_object_t, void *, bool *); -int list_strings_sep_in_array(prop_object_t, void *, bool *); -size_t find_longest_pkgver(prop_dictionary_t); -void print_package_line(const char *); /* From find-files.c */ int repo_find_files_in_packages(const char *); diff --git a/bin/xbps-repo/main.c b/bin/xbps-repo/main.c index 6c8633b1..a74b36bf 100644 --- a/bin/xbps-repo/main.c +++ b/bin/xbps-repo/main.c @@ -34,6 +34,7 @@ #include #include "defs.h" +#include "../xbps-bin/defs.h" static void __attribute__((noreturn)) usage(void) diff --git a/bin/xbps-repo/repository.c b/bin/xbps-repo/repository.c index 8cba9ecc..582a24bf 100644 --- a/bin/xbps-repo/repository.c +++ b/bin/xbps-repo/repository.c @@ -165,6 +165,7 @@ int register_repository(const char *uri) { struct repoinfo *rpi = NULL; + struct xbps_fetch_progress_data xfpd; const char *idxstr = NULL; char *metadir, *plist; int rv = 0; @@ -174,7 +175,8 @@ register_repository(const char *uri) if (xbps_check_is_repo_string_remote(idxstr)) { printf("Fetching remote package index at %s...\n", idxstr); - rv = xbps_repository_sync_pkg_index(idxstr); + rv = xbps_repository_sync_pkg_index(idxstr, + fetch_file_progress_cb, &xfpd); if (rv == -1) { fprintf(stderr, "E: could not fetch pkg index file: %s.\n", @@ -296,6 +298,7 @@ show_pkg_deps_from_repolist(const char *pkgname) static int repo_sync_pkg_index_cb(struct repository_pool_index *rpi, void *arg, bool *done) { + struct xbps_fetch_progress_data xfpd; struct repoinfo *rp; char *plist; int rv = 0; @@ -307,7 +310,8 @@ repo_sync_pkg_index_cb(struct repository_pool_index *rpi, void *arg, bool *done) return 0; printf("Syncing package index from: %s\n", rpi->rpi_uri); - rv = xbps_repository_sync_pkg_index(rpi->rpi_uri); + rv = xbps_repository_sync_pkg_index(rpi->rpi_uri, + fetch_file_progress_cb, &xfpd); if (rv == -1) { fprintf(stderr, "E: returned: %s\n", xbps_fetch_error_string()); return rv; diff --git a/bin/xbps-uhelper/Makefile b/bin/xbps-uhelper/Makefile index 64c06d33..f338e9ba 100644 --- a/bin/xbps-uhelper/Makefile +++ b/bin/xbps-uhelper/Makefile @@ -2,5 +2,6 @@ TOPDIR = ../.. -include $(TOPDIR)/config.mk BIN = xbps-uhelper +OBJS = main.o ../xbps-bin/fetch.o include $(TOPDIR)/prog.mk diff --git a/bin/xbps-uhelper/main.c b/bin/xbps-uhelper/main.c index 771040e0..1d1ebcbb 100644 --- a/bin/xbps-uhelper/main.c +++ b/bin/xbps-uhelper/main.c @@ -33,6 +33,7 @@ #include #include +#include "../xbps-bin/defs.h" /* error messages in bold/red */ #define MSG_ERROR "\033[1m\033[31m" @@ -103,6 +104,7 @@ usage(void) int main(int argc, char **argv) { + struct xbps_fetch_progress_data xfpd; prop_dictionary_t dict; const char *version; char *plist, *pkgname, *pkgver, *in_chroot_env, *hash; @@ -321,7 +323,8 @@ main(int argc, char **argv) usage(); for (i = 1; i < argc; i++) { - rv = xbps_fetch_file(argv[i], ".", false, "v"); + rv = xbps_fetch_file(argv[i], ".", false, "v", + fetch_file_progress_cb, &xfpd); if (rv == -1) { printf("%s: %s\n", argv[1], xbps_fetch_error_string()); diff --git a/include/xbps_api.h b/include/xbps_api.h index dca262f0..7348b9ed 100644 --- a/include/xbps_api.h +++ b/include/xbps_api.h @@ -30,6 +30,8 @@ #include #include +#include +#include #include #ifdef __cplusplus @@ -117,8 +119,11 @@ __BEGIN_DECLS +void xbps_printf(const char *, ...); void xbps_dbg_printf(const char *, ...); void xbps_dbg_printf_append(const char *, ...); +void xbps_error_printf(const char *, ...); +void xbps_warn_printf(const char *, ...); /** @addtogroup initend */ /*@{*/ @@ -191,6 +196,72 @@ int xbps_cmpver(const char *pkg1, const char *pkg2); /** @addtogroup download */ /*@{*/ +/** + * @struct xbps_fetch_progress_data xbps_api.h "xbps_api.h" + * @brief Structure to be passed to the fetch progress function callback. + * + * This structure is passed as argument to the fetch progress function + * callback and its members will be updated when there's any progress. + * All members marked as read-only in this struct are set internally by + * xbps_unpack_binary_pkg() and shouldn't be modified in the passed + * function callback. + */ +struct xbps_fetch_progress_data { + /** + * @var file_size + * + * Filename size for the file to be fetched (set internally, + * read only). + */ + off_t file_size; + /** + * @var file_offset + * + * Current offset for the filename being fetched (set internally, + * read only). + */ + off_t file_offset; + /** + * @var file_dloaded + * + * Bytes downloaded for the file being fetched (set internally). + */ + off_t file_dloaded; + /** + * @var file_name + * + * File name being fetched (set internally, read only). + */ + const char *file_name; + /** + * @var cb_start + * + * If true the function callback should be prepared to start + * the transfer progress (set internally, read only). + */ + bool cb_start; + /** + * var cb_update + * + * If true the function callback should be prepared to + * update the transfer progress (set internally, read only). + */ + bool cb_update; + /** + * var cb_end + * + * If true the function callback should be prepated to + * end the transfer progress (set internally, read only). + */ + bool cb_end; + /** + * @var cookie + * + * Pointer to private user data. + */ + void *cookie; +}; + /** * Download a file from a remote URL. * @@ -199,6 +270,10 @@ int xbps_cmpver(const char *pkg1, const char *pkg2); * @param[in] refetch If true and local/remote size/mtime do not match, * fetch the file from scratch. * @param[in] flags Flags passed to libfetch's fetchXget(). + * @param[in] progress_cb Pointer to a function callback to update the + * the fetch progress. + * @param[in] xfpd Pointer to a xbps_fetch_progress_data struct to be + * passed to the \a progress_cb callback as its argument. * * @return -1 on error, 0 if not downloaded (because local/remote size/mtime * do not match) and 1 if downloaded successfully. @@ -206,7 +281,9 @@ int xbps_cmpver(const char *pkg1, const char *pkg2); int xbps_fetch_file(const char *uri, const char *outputdir, bool refetch, - const char *flags); + const char *flags, + void (*progress_cb)(void *), + struct xbps_fetch_progress_data *xfpd); /** * Returns last error string reported by xbps_fetch_file(). @@ -217,19 +294,6 @@ const char *xbps_fetch_error_string(void); /*@}*/ -int xbps_humanize_number(char *, int64_t); - -/** - * @ingroup dircreate - * - * Creates a directory (and required components if necessary). - * - * @param[in] path Path for final directory. - * @param[in] mode Mode for final directory (0755 if not specified). - * - * @return 0 on success, -1 on error and errno set appropiately. - */ -int xbps_mkpath(char *path, mode_t mode); /** * @ingroup pkg_orphans @@ -774,12 +838,18 @@ prop_dictionary_t * by the \a uri argument (if necessary). * * @param[in] uri URI to a remote repository. + * @param[in] progress_cb Pointer to a function callback to update the + * the fetch progress. + * @param[in] xfpd Pointer to a xbps_fetch_progress_data struct to be + * passed to the \a progress_cb callback as its argument. * * @return -1 on error (errno is set appropiately), 0 if transfer was * not necessary (local/remote size/mtime matched) or 1 if * downloaded successfully. */ -int xbps_repository_sync_pkg_index(const char *uri); +int xbps_repository_sync_pkg_index(const char *uri, + void (*progress_cb)(void *), + struct xbps_fetch_progress_data *xfpd); /*@}*/ @@ -857,21 +927,90 @@ int xbps_set_pkg_state_dictionary(prop_dictionary_t dict, pkg_state_t state); /*@}*/ +/** @addtogroup unpack */ +/*@{*/ + /** - * @ingroup unpack + * @struct xbps_unpack_progress_data xbps_api.h "xbps_api.h" + * @brief Structure to be passed to the unpacking progress function callback. * + * This structure is passed as argument to the unpack progress function + * callback and its members will be updated when there's any progress. + * All members in this struct are set internally by xbps_unpack_binary_pkg() + * and should be used in read-only mode in the function callback. + */ +struct xbps_unpack_progress_data { + /** + * @var entry + * + * Entry pathname string (set internally, read only). + */ + const char *entry; + /** + * @var entry_size + * + * Entry file size (set internally, read only). + */ + int64_t entry_size; + /** + * @var entry_extract_count + * + * Total number of extracted entries (set internally, read only). + */ + ssize_t entry_extract_count; + /** + * @var entry_total_count + * + * Total number of entries in package (set internally, read only). + */ + ssize_t entry_total_count; + /** + * @var entry_is_metadata + * + * If true "entry" is a package metadata file (set internally, + * read only). + */ + bool entry_is_metadata; + /** + * @var entry_is_conf + * + * If true "entry" is a configuration file (set internally, + * read only). + */ + bool entry_is_conf; +}; + +/** * Unpacks a binary package into specified root directory. * * @param[in] trans_pkg_dict Package proplib dictionary as stored in the * \a packages array returned by the transaction dictionary. + * @param[in] progress_cb Pointer to a function callback to update progress data + * while extracting files in package (optional). + * @param[in] xupd Pointer to a struct xbps_unpack_progress_data to be passed to + * the function callback \a progress_cb. * * @return 0 on success, otherwise an errno value. */ -int xbps_unpack_binary_pkg(prop_dictionary_t trans_pkg_dict); +int xbps_unpack_binary_pkg(prop_dictionary_t trans_pkg_dict, + void (*progress_cb)(void *), + struct xbps_unpack_progress_data *xupd); + +/*@}*/ /** @addtogroup util */ /*@{*/ +/** + * Creates a directory (and required components if necessary). + * + * @param[in] path Path for final directory. + * @param[in] mode Mode for final directory (0755 if not specified). + * + * @return 0 on success, -1 on error and errno set appropiately. + */ +int xbps_mkpath(char *path, mode_t mode); + /** * Returns a string by concatenating its variable argument list * as specified by the format string \a fmt. @@ -1070,6 +1209,18 @@ void xbps_set_cachedir(const char *cachedir); */ const char *xbps_get_cachedir(void); +/** + * Converts the 64 bits signed number specified in \a bytes to + * a human parsable string buffer pointed to \a buf. + * + * @param[out] buf Buffer to store the resulting string. At least + * it should have space for 6 chars. + * @param[in] bytes 64 bits signed number to convert. + * + * @return A negative number is returned on error, 0 otherwise. + */ +int xbps_humanize_number(char *buf, int64_t bytes); + /** * Sets the flag specified in \a flags for internal use. * diff --git a/lib/download.c b/lib/download.c index 57312fe0..9098b9af 100644 --- a/lib/download.c +++ b/lib/download.c @@ -47,146 +47,10 @@ /** * @file lib/download.c * @brief Download routines - * @defgroup download Internal download functions + * @defgroup download Download functions * - * These functions allow you to download files. + * XBPS download related functions, frontend for NetBSD's libfetch. */ -struct xferstat { - struct timeval start; - struct timeval last; - off_t size; - off_t offset; - off_t rcvd; - const char *name; -}; - -/* - * Compute and display ETA - */ -static const char * -stat_eta(struct xferstat *xsp) -{ - static char str[16]; - long elapsed, eta; - off_t received, expected; - - elapsed = xsp->last.tv_sec - xsp->start.tv_sec; - received = xsp->rcvd - xsp->offset; - expected = xsp->size - xsp->rcvd; - eta = (long)(elapsed * expected / received); - if (eta > 3600) - snprintf(str, sizeof str, "%02ldh%02ldm", - eta / 3600, (eta % 3600) / 60); - else - snprintf(str, sizeof str, "%02ldm%02lds", - eta / 60, eta % 60); - return str; -} - -/** High precision double comparison - * \param[in] a The first double to compare - * \param[in] b The second double to compare - * \return true on equal within precison, false if not equal within defined precision. - */ -static inline bool -compare_double(const double a, const double b) -{ - const double precision = 0.00001; - - if ((a - precision) < b && (a + precision) > b) - return true; - else - return false; -} - -/* - * Compute and display transfer rate - */ -static const char * -stat_bps(struct xferstat *xsp) -{ - static char str[16]; - char size[8]; - double delta, bps; - - delta = (xsp->last.tv_sec + (xsp->last.tv_usec / 1.e6)) - - (xsp->start.tv_sec + (xsp->start.tv_usec / 1.e6)); - if (compare_double(delta, 0.0001)) { - snprintf(str, sizeof str, "-- stalled --"); - } else { - bps = ((double)(xsp->rcvd - xsp->offset) / delta); - (void)xbps_humanize_number(size, (int64_t)bps); - snprintf(str, sizeof str, "%s/s", size); - } - return str; -} - -/* - * Update the stats display - */ -static void -stat_display(struct xferstat *xsp) -{ - struct timeval now; - char totsize[8], recvsize[8]; - - gettimeofday(&now, NULL); - if (now.tv_sec <= xsp->last.tv_sec) - return; - xsp->last = now; - - (void)xbps_humanize_number(totsize, (int64_t)xsp->size); - (void)xbps_humanize_number(recvsize, (int64_t)xsp->rcvd); - fprintf(stderr, "\r%s: %s [%d%% of %s]", - xsp->name, recvsize, - (int)((double)(100.0 * (double)xsp->rcvd) / (double)xsp->size), - totsize); - fprintf(stderr, " %s", stat_bps(xsp)); - if (xsp->size > 0 && xsp->rcvd > 0 && - xsp->last.tv_sec >= xsp->start.tv_sec + 10) - fprintf(stderr, " ETA: %s", stat_eta(xsp)); - fprintf(stderr, "\033[K"); -} - -/* - * Initialize the transfer statistics - */ -static void -stat_start(struct xferstat *xsp, const char *name, off_t *size, off_t *offset) -{ - gettimeofday(&xsp->start, NULL); - xsp->last.tv_sec = xsp->last.tv_usec = 0; - xsp->name = name; - xsp->size = *size; - xsp->offset = *offset; - xsp->rcvd = *offset; -} - -/* - * Update the transfer statistics - */ -static void -stat_update(struct xferstat *xsp, off_t rcvd) -{ - xsp->rcvd = rcvd + xsp->offset; - stat_display(xsp); -} - -/* - * Finalize the transfer statistics - */ -static void -stat_end(struct xferstat *xsp) -{ - char size[8]; - - (void)xbps_humanize_number(size, (int64_t)xsp->size); - fprintf(stderr, "\rDownloaded %s for %s [avg rate: %s]", - size, xsp->name, stat_bps(xsp)); - fprintf(stderr, "\033[K\n"); -} - -#ifdef DEBUG static const char * print_time(time_t *t) { @@ -197,13 +61,6 @@ print_time(time_t *t) strftime(buf, sizeof(buf), "%d %b %Y %H:%M", &tm); return buf; } -#endif - -const char * -xbps_fetch_error_string(void) -{ - return fetchLastErrString; -} void HIDDEN xbps_fetch_set_cache_connection(int global, int per_host) @@ -222,18 +79,27 @@ xbps_fetch_unset_cache_connection(void) fetchConnectionCacheClose(); } +const char * +xbps_fetch_error_string(void) +{ + return fetchLastErrString; +} + int -xbps_fetch_file(const char *uri, const char *outputdir, bool refetch, - const char *flags) +xbps_fetch_file(const char *uri, + const char *outputdir, + bool refetch, + const char *flags, + void (*progress_cb)(void *), + struct xbps_fetch_progress_data *xfpd) { struct stat st; - struct xferstat xs; struct url *url = NULL; struct url_stat url_st; struct fetchIO *fio = NULL; struct timeval tv[2]; + off_t bytes_dload = -1; ssize_t bytes_read = -1, bytes_written; - off_t bytes_dld = -1; char buf[4096], *filename, *destfile = NULL; int fd = -1, rv = 0; bool restart = false; @@ -244,11 +110,10 @@ xbps_fetch_file(const char *uri, const char *outputdir, bool refetch, /* * Get the filename specified in URI argument. */ - filename = strrchr(uri, '/'); - if (filename == NULL) { - errno = EINVAL; + if ((filename = strrchr(uri, '/')) == NULL) return -1; - } + + /* Skip first '/' */ filename++; /* * Compute destination file path. @@ -261,7 +126,6 @@ xbps_fetch_file(const char *uri, const char *outputdir, bool refetch, /* * Check if we have to resume a transfer. */ - memset(&st, 0, sizeof(st)); if (stat(destfile, &st) == 0) { if (st.st_size > 0) restart = true; @@ -324,23 +188,23 @@ xbps_fetch_file(const char *uri, const char *outputdir, bool refetch, url->offset = st.st_size; fio = fetchXGet(url, &url_st, flags); } -#ifdef DEBUG - printf("st.st_size: %zd\n", (ssize_t)st.st_size); - printf("st.st_atime: %s\n", print_time(&st.st_atime)); - printf("st.st_mtime: %s\n", print_time(&st.st_mtime)); - printf("url->scheme: %s\n", url->scheme); - printf("url->host: %s\n", url->host); - printf("url->port: %d\n", url->port); - printf("url->doc: %s\n", url->doc); - printf("url->offset: %zd\n", (ssize_t)url->offset); - printf("url->length: %zu\n", url->length); - printf("url->last_modified: %s\n", print_time(&url->last_modified)); + /* debug stuff */ + xbps_dbg_printf("st.st_size: %zd\n", (ssize_t)st.st_size); + xbps_dbg_printf("st.st_atime: %s\n", print_time(&st.st_atime)); + xbps_dbg_printf("st.st_mtime: %s\n", print_time(&st.st_mtime)); + xbps_dbg_printf("url->scheme: %s\n", url->scheme); + xbps_dbg_printf("url->host: %s\n", url->host); + xbps_dbg_printf("url->port: %d\n", url->port); + xbps_dbg_printf("url->doc: %s\n", url->doc); + xbps_dbg_printf("url->offset: %zd\n", (ssize_t)url->offset); + xbps_dbg_printf("url->length: %zu\n", url->length); + xbps_dbg_printf("url->last_modified: %s\n", + print_time(&url->last_modified)); + xbps_dbg_printf("url_stat.size: %zd\n", (ssize_t)url_st.size); + xbps_dbg_printf("url_stat.atime: %s\n", print_time(&url_st.atime)); + xbps_dbg_printf("url_stat.mtime: %s\n", print_time(&url_st.mtime)); - printf("url_stat.size: %zd\n", (ssize_t)url_st.size); - printf("url_stat.atime: %s\n", print_time(&url_st.atime)); - printf("url_stat.mtime: %s\n", print_time(&url_st.mtime)); -#endif if (fio == NULL && fetchLastErrCode != FETCH_OK) { if (!refetch && restart && fetchLastErrCode == FETCH_UNAVAIL) { /* @@ -356,13 +220,13 @@ xbps_fetch_file(const char *uri, const char *outputdir, bool refetch, goto out; } if (url_st.size == -1) { - printf("Remote file size is unknown!\n"); + xbps_error_printf("Remote file size is unknown!\n"); errno = EINVAL; rv = -1; goto out; } else if (st.st_size > url_st.size) { - printf("Local file %s is greater than remote file!\n", - filename); + xbps_error_printf("Local file %s is greater than remote " + "file!\n", filename); errno = EFBIG; rv = -1; goto out; @@ -371,8 +235,6 @@ xbps_fetch_file(const char *uri, const char *outputdir, bool refetch, /* Local and remote size/mtime match, do nothing. */ goto out; } - fprintf(stderr, "Connected to %s.\n", url->host); - /* * If restarting, open the file for appending otherwise create it. */ @@ -386,32 +248,64 @@ xbps_fetch_file(const char *uri, const char *outputdir, bool refetch, goto out; } + /* + * Initialize data for the fetch progress function callback + * and let the user know that the transfer is going to start + * immediately. + */ + if (progress_cb != NULL && xfpd != NULL) { + xfpd->file_name = filename; + xfpd->file_size = url_st.size; + xfpd->file_offset = url->offset; + xfpd->file_dloaded = -1; + xfpd->cb_start = true; + xfpd->cb_update = false; + xfpd->cb_end = false; + progress_cb(xfpd); + } /* * Start fetching requested file. */ - stat_start(&xs, filename, &url_st.size, &url->offset); while ((bytes_read = fetchIO_read(fio, buf, sizeof(buf))) > 0) { bytes_written = write(fd, buf, (size_t)bytes_read); if (bytes_written != bytes_read) { - fprintf(stderr, "Couldn't write to %s!\n", destfile); + xbps_error_printf("Couldn't write to %s!\n", destfile); rv = -1; goto out; } - bytes_dld += bytes_read; - stat_update(&xs, bytes_dld); + bytes_dload += bytes_read; + /* + * Let the fetch progress callback know that + * we are sucking more bytes from it. + */ + if (progress_cb != NULL && xfpd != NULL) { + xfpd->file_dloaded = bytes_dload; + xfpd->cb_start = false; + xfpd->cb_update = true; + progress_cb(xfpd); + } + + } + /* + * Let the fetch progress callback know that the file + * has been fetched. + */ + if (progress_cb != NULL && xfpd != NULL) { + xfpd->cb_update = false; + xfpd->cb_end = true; + progress_cb(xfpd); } if (bytes_read == -1) { - fprintf(stderr, "IO error while fetching %s: %s\n", filename, + xbps_error_printf("IO error while fetching %s: %s\n", filename, fetchLastErrString); errno = EIO; rv = -1; goto out; } - stat_end(&xs); - - if (fd == -1) + if (fd == -1) { + rv = -1; goto out; - + } /* * Update mtime in local file to match remote file if transfer * was successful. @@ -419,13 +313,14 @@ xbps_fetch_file(const char *uri, const char *outputdir, bool refetch, tv[0].tv_sec = url_st.atime ? url_st.atime : url_st.mtime; tv[1].tv_sec = url_st.mtime; tv[0].tv_usec = tv[1].tv_usec = 0; - if (utimes(destfile, tv) == -1) + if (utimes(destfile, tv) == -1) { rv = -1; - else { - /* File downloaded successfully */ - rv = 1; + goto out; } + /* File downloaded successfully */ + rv = 1; + out: if (fd != -1) (void)close(fd); diff --git a/lib/fexec.c b/lib/fexec.c index 1680fdfc..bfb11bf7 100644 --- a/lib/fexec.c +++ b/lib/fexec.c @@ -37,16 +37,6 @@ #include #include "xbps_api_impl.h" -/** - * @file lib/fexec.c - * @brief Generic file execution routines - * @defgroup fexec File execution functions - * - * These functions will fork and execute a program in cwd (current working - * directory), destination directory or changing root directory to - * destination directory and passing an arbitrary number of arguments to it. - */ - static int pfcexec(const char *path, const char *file, const char **argv) { diff --git a/lib/initend.c b/lib/initend.c index 645ebf71..ff4ae569 100644 --- a/lib/initend.c +++ b/lib/initend.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2010 Juan Romero Pardines. + * Copyright (c) 2011 Juan Romero Pardines. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -32,6 +32,14 @@ #include #include "xbps_api_impl.h" +/** + * @file lib/initend.c + * @brief Initialization and finalization routines + * @defgroup initend Initialization and finalization functions + * + * Use these functions to initialize some parameters before starting + * using libxbps and finalize usage to release resources at the end. + */ static bool with_debug; void @@ -50,18 +58,13 @@ xbps_end(void) xbps_fetch_unset_cache_connection(); } -void -xbps_dbg_printf(const char *fmt, ...) +static void +common_printf(FILE *f, const char *msg, const char *fmt, va_list ap) { - va_list ap; + if (msg != NULL) + fprintf(f, "%s", msg); - if (!with_debug) - return; - - va_start(ap, fmt); - fprintf(stderr, "[DEBUG] "); - vfprintf(stderr, fmt, ap); - va_end(ap); + vfprintf(f, fmt, ap); } void @@ -73,6 +76,53 @@ xbps_dbg_printf_append(const char *fmt, ...) return; va_start(ap, fmt); - vfprintf(stderr, fmt, ap); + common_printf(stderr, NULL, fmt, ap); + va_end(ap); +} + +void +xbps_dbg_printf(const char *fmt, ...) +{ + va_list ap; + + if (!with_debug) + return; + + va_start(ap, fmt); + common_printf(stderr, "[DEBUG] ", fmt, ap); + va_end(ap); +} + +void +xbps_error_printf(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + common_printf(stderr, "ERROR: ", fmt, ap); + va_end(ap); +} + +void +xbps_warn_printf(const char *fmt, ...) +{ + int flags = xbps_get_flags(); + va_list ap; + + if ((flags & XBPS_FLAG_VERBOSE) == 0) + return; + + va_start(ap, fmt); + common_printf(stderr, "WARNING: ", fmt, ap); + va_end(ap); +} + +void +xbps_printf(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + common_printf(stdout, NULL, fmt, ap); va_end(ap); } diff --git a/lib/mkpath.c b/lib/mkpath.c index 968778b7..c1e400fe 100644 --- a/lib/mkpath.c +++ b/lib/mkpath.c @@ -38,12 +38,6 @@ #include -/** - * @file lib/mkpath.c - * @brief Generic directory creation routines - * @defgroup dircreate Generic directory creation functions - */ - int xbps_mkpath(char *path, mode_t mode) { diff --git a/lib/package_config_files.c b/lib/package_config_files.c index 41e14fcd..9902cb16 100644 --- a/lib/package_config_files.c +++ b/lib/package_config_files.c @@ -199,7 +199,7 @@ xbps_entry_install_conf_file(prop_dictionary_t filesd, } else if ((strcmp(sha256_orig, sha256_cur) == 0) && (strcmp(sha256_orig, sha256_new)) && (strcmp(sha256_cur, sha256_new))) { - printf("Updating configuration file `%s' " + xbps_printf("Updating configuration file `%s' " "with new version.\n", cffile); rv = 1; break; @@ -211,7 +211,7 @@ xbps_entry_install_conf_file(prop_dictionary_t filesd, } else if ((strcmp(sha256_orig, sha256_new) == 0) && (strcmp(sha256_cur, sha256_new)) && (strcmp(sha256_orig, sha256_cur))) { - printf("Keeping modified configuration file " + xbps_printf("Keeping modified configuration file " "`%s'.\n", cffile); rv = 0; break; @@ -241,9 +241,9 @@ xbps_entry_install_conf_file(prop_dictionary_t filesd, rv = -1; break; } - printf("Keeping modified configuration file " + xbps_printf("Keeping modified configuration file " "`%s'.\n", cffile); - printf("Installing new configuration file as " + xbps_printf("Installing new configuration file as " "`%s.new-%s'\n", cffile, version); archive_entry_set_pathname(entry, buf); free(buf); diff --git a/lib/package_configure.c b/lib/package_configure.c index aecfe72a..7b89a017 100644 --- a/lib/package_configure.c +++ b/lib/package_configure.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2009-2010 Juan Romero Pardines. + * Copyright (c) 2009-2011 Juan Romero Pardines. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -115,7 +115,7 @@ xbps_configure_pkg(const char *pkgname, lver = version; } - printf("%sonfiguring package %s-%s...\n", + xbps_printf("%sonfiguring package `%s-%s' ...\n", reconfigure ? "Rec" : "C", pkgname, lver); buf = xbps_xasprintf(".%s/metadata/%s/INSTALL", @@ -134,8 +134,8 @@ xbps_configure_pkg(const char *pkgname, if (xbps_file_exec(buf, "post", pkgname, lver, update ? "yes" : "no", NULL) != 0) { free(buf); - xbps_dbg_printf("%s: [configure] post INSTALL " - "action returned %s\n", pkgname, strerror(errno)); + xbps_error_printf("%s: post install script error: %s\n", + pkgname, strerror(errno)); return errno; } } else { diff --git a/lib/package_orphans.c b/lib/package_orphans.c index 29f517bf..d8ad45ea 100644 --- a/lib/package_orphans.c +++ b/lib/package_orphans.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2009-2010 Juan Romero Pardines. + * Copyright (c) 2009-2011 Juan Romero Pardines. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,7 +31,6 @@ #include #include "xbps_api_impl.h" -#include "queue.h" /** * @file lib/package_orphans.c @@ -61,29 +60,19 @@ * dictionary. */ -struct orphan_pkg { - SIMPLEQ_ENTRY(orphan_pkg) chain; - prop_dictionary_t dict; - const char *pkgname; -}; - -static SIMPLEQ_HEAD(orphan_head, orphan_pkg) orphan_list = - SIMPLEQ_HEAD_INITIALIZER(orphan_list); - static int find_orphan_pkg(prop_object_t obj, void *arg, bool *loop_done) { - prop_array_t reqby; - prop_object_t obj2; - prop_object_iterator_t iter; - struct orphan_pkg *orphan; + prop_array_t reqby, orphans = arg; + prop_object_t obj2, obj3; + prop_object_iterator_t iter, iter2; + const char *orphan_pkgname; char *pkgname; unsigned int ndep = 0, cnt = 0; bool automatic = false; pkg_state_t state = 0; int rv = 0; - (void)arg; (void)loop_done; prop_dictionary_get_bool(obj, "automatic-install", &automatic); @@ -97,100 +86,79 @@ find_orphan_pkg(prop_object_t obj, void *arg, bool *loop_done) return 0; reqby = prop_dictionary_get(obj, "requiredby"); - if (reqby == NULL) - return 0; - else if (prop_object_type(reqby) != PROP_TYPE_ARRAY) + if (prop_object_type(reqby) != PROP_TYPE_ARRAY) return EINVAL; - if ((cnt = prop_array_count(reqby)) == 0) - goto add_orphan; + if ((cnt = prop_array_count(reqby)) == 0) { + prop_array_add(orphans, obj); + return 0; + } iter = prop_array_iterator(reqby); if (iter == NULL) - return errno; + return ENOMEM; while ((obj2 = prop_object_iterator_next(iter)) != NULL) { pkgname = xbps_get_pkg_name(prop_string_cstring_nocopy(obj2)); - if (pkgname == NULL) + if (pkgname == NULL) { + prop_object_iterator_release(iter); return EINVAL; + } - SIMPLEQ_FOREACH(orphan, &orphan_list, chain) { - if (strcmp(orphan->pkgname, pkgname) == 0) { + iter2 = prop_array_iterator(orphans); + if (iter == NULL) { + free(pkgname); + prop_object_iterator_release(iter); + return ENOMEM; + } + while ((obj3 = prop_object_iterator_next(iter2)) != NULL) { + prop_dictionary_get_cstring_nocopy(obj3, + "pkgname", &orphan_pkgname); + if (strcmp(orphan_pkgname, pkgname) == 0) { ndep++; break; } } + prop_object_iterator_release(iter2); free(pkgname); } prop_object_iterator_release(iter); + if (ndep != cnt) return 0; - -add_orphan: - orphan = NULL; - orphan = malloc(sizeof(struct orphan_pkg)); - if (orphan == NULL) - return errno; - - prop_dictionary_get_cstring_nocopy(obj, "pkgname", &orphan->pkgname); - orphan->dict = prop_dictionary_copy(obj); - SIMPLEQ_INSERT_TAIL(&orphan_list, orphan, chain); + if (!prop_array_add(orphans, obj)) + return EINVAL; return 0; } -static void -cleanup(void) -{ - struct orphan_pkg *orphan; - - while ((orphan = SIMPLEQ_FIRST(&orphan_list)) != NULL) { - SIMPLEQ_REMOVE(&orphan_list, orphan, orphan_pkg, chain); - prop_object_release(orphan->dict); - free(orphan); - } - xbps_regpkgdb_dictionary_release(); -} - prop_array_t xbps_find_orphan_packages(void) { prop_array_t array; prop_dictionary_t dict; - struct orphan_pkg *orphan; int rv = 0; if ((dict = xbps_regpkgdb_dictionary_get()) == NULL) return NULL; + /* + * Prepare an array with all packages previously found. + */ + if ((array = prop_array_create()) == NULL) + return NULL; + /* * Find out all orphans by looking at the * regpkgdb dictionary and iterate in reverse order * in which packages were installed. */ rv = xbps_callback_array_iter_reverse_in_dict(dict, "packages", - find_orphan_pkg, NULL); + find_orphan_pkg, array); if (rv != 0) { errno = rv; - cleanup(); + prop_object_release(array); return NULL; } - /* - * Prepare an array with all packages previously found. - */ - array = prop_array_create(); - if (array == NULL) { - cleanup(); - return NULL; - } - while ((orphan = SIMPLEQ_FIRST(&orphan_list)) != NULL) { - if (!prop_array_add(array, orphan->dict)) { - cleanup(); - return NULL; - } - SIMPLEQ_REMOVE(&orphan_list, orphan, orphan_pkg, chain); - prop_object_release(orphan->dict); - free(orphan); - } xbps_regpkgdb_dictionary_release(); return array; diff --git a/lib/package_purge.c b/lib/package_purge.c index 3ef78a8c..847e7305 100644 --- a/lib/package_purge.c +++ b/lib/package_purge.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2009-2010 Juan Romero Pardines. + * Copyright (c) 2009-2011 Juan Romero Pardines. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -54,7 +54,7 @@ remove_pkg_metadata(const char *pkgname) struct dirent *dp; DIR *dirp; char *metadir, *path; - int rv = 0, flags = xbps_get_flags(); + int rv = 0; assert(pkgname != NULL); @@ -81,11 +81,10 @@ remove_pkg_metadata(const char *pkgname) return ENOMEM; } - if (unlink(path) == -1) { - if (flags & XBPS_FLAG_VERBOSE) - printf("WARNING: can't remove %s (%s)\n", - pkgname, strerror(errno)); - } + if (unlink(path) == -1) + xbps_warn_printf("can't remove metadata file: " + "`%s': %s\n", dp->d_name, strerror(errno)); + free(path); } (void)closedir(dirp); @@ -130,7 +129,7 @@ int xbps_purge_pkg(const char *pkgname, bool check_state) { prop_dictionary_t dict, pkgd; - int rv = 0, flags = xbps_get_flags(); + int rv = 0; pkg_state_t state = 0; assert(pkgname != NULL); @@ -188,8 +187,7 @@ xbps_purge_pkg(const char *pkgname, bool check_state) if ((rv = xbps_unregister_pkg(pkgname)) != 0) goto out; - if (flags & XBPS_FLAG_VERBOSE) - printf("Package %s purged successfully.\n", pkgname); + xbps_printf("Package %s purged successfully.\n", pkgname); out: xbps_regpkgdb_dictionary_release(); diff --git a/lib/package_remove.c b/lib/package_remove.c index 847ab38f..e9dd7f4c 100644 --- a/lib/package_remove.c +++ b/lib/package_remove.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2009-2010 Juan Romero Pardines. + * Copyright (c) 2009-2011 Juan Romero Pardines. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -123,31 +123,28 @@ xbps_remove_pkg_files(prop_dictionary_t dict, const char *key) "sha256", &sha256); rv = xbps_check_file_hash(path, sha256); if (rv == ENOENT) { - fprintf(stderr, - "WARNING: '%s' doesn't exist!\n", file); + xbps_warn_printf("'%s' doesn't exist!\n", file); free(path); rv = 0; continue; } else if (rv == ERANGE) { rv = 0; - if (flags & XBPS_FLAG_VERBOSE) { - if (flags & XBPS_FLAG_FORCE) { - fprintf(stderr, - "WARNING: '%s' SHA256 " - "mismatch, forcing " - "removal...\n", file); - } else { - fprintf(stderr, - "WARNING: '%s' SHA256 " - "mismatch, preserving...\n", - file); - } + if (flags & XBPS_FLAG_FORCE) { + xbps_warn_printf("'%s': SHA256 " + "mismatch, forcing removal...\n", + file); + } else { + xbps_warn_printf("'%s': SHA256 " + "mismatch, preserving file...\n", + file); } if ((flags & XBPS_FLAG_FORCE) == 0) { free(path); continue; } } else if (rv != 0 && rv != ERANGE) { + xbps_error_printf("failed to check hash for " + "`%s': %s\n", file, strerror(rv)); free(path); break; } @@ -156,15 +153,12 @@ xbps_remove_pkg_files(prop_dictionary_t dict, const char *key) * Remove the object if possible. */ if (remove(path) == -1) { - if (flags & XBPS_FLAG_VERBOSE) - fprintf(stderr, - "WARNING: can't remove %s %s " - "(%s)\n", curobj, file, strerror(errno)); + xbps_warn_printf("can't remove %s `%s': %s\n", + curobj, file, strerror(errno)); } else { /* Success */ - if (flags & XBPS_FLAG_VERBOSE) - printf("Removed %s: %s\n", curobj, file); + xbps_printf("Removed %s: `%s'\n", curobj, file); } free(path); } @@ -207,8 +201,7 @@ xbps_remove_pkg(const char *pkgname, const char *version, bool update) rmfile_exists = true; if (xbps_file_exec(buf, "pre", pkgname, version, update ? "yes" : "no", NULL) != 0) { - fprintf(stderr, - "%s: prerm action target error (%s)\n", + xbps_error_printf("%s: pre remove script error: %s\n", pkgname, strerror(errno)); free(buf); return errno; @@ -265,9 +258,8 @@ xbps_remove_pkg(const char *pkgname, const char *version, bool update) */ if (rmfile_exists && (xbps_file_exec(buf, "post", pkgname, version, "no", NULL) != 0)) { - fprintf(stderr, - "%s: postrm action target error (%s)\n", pkgname, - strerror(errno)); + xbps_error_printf("%s: post remove script error: %s\n", + pkgname, strerror(errno)); free(buf); return errno; } diff --git a/lib/package_remove_obsoletes.c b/lib/package_remove_obsoletes.c index f22209f7..298d6e39 100644 --- a/lib/package_remove_obsoletes.c +++ b/lib/package_remove_obsoletes.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2009-2010 Juan Romero Pardines. + * Copyright (c) 2009-2011 Juan Romero Pardines. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -126,14 +126,13 @@ again: * Obsolete obj found, remove it. */ if (remove(file) == -1) { - fprintf(stderr, - "WARNING: couldn't remove obsolete obj: %s (%s)\n", - prop_string_cstring_nocopy(oldstr), + xbps_warn_printf("couldn't remove obsole entry " + "`%s': %s\n", prop_string_cstring_nocopy(oldstr), strerror(errno)); free(file); continue; } - printf("Removed obsolete obj: %s\n", + xbps_printf("Removed obsolete entry: %s\n", prop_string_cstring_nocopy(oldstr)); free(file); } diff --git a/lib/package_state.c b/lib/package_state.c index f59580cd..bbfd5413 100644 --- a/lib/package_state.c +++ b/lib/package_state.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2009-2010 Juan Romero Pardines. + * Copyright (c) 2009-2011 Juan Romero Pardines. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -32,6 +32,21 @@ #include #include "xbps_api_impl.h" +struct state { + const char *string; + pkg_state_t number; +}; + +static const struct state states[] = { + { "unpacked", XBPS_PKG_STATE_UNPACKED }, + { "installed", XBPS_PKG_STATE_INSTALLED }, + { "broken", XBPS_PKG_STATE_BROKEN }, + { "config-files", XBPS_PKG_STATE_CONFIG_FILES }, + { "not-installed", XBPS_PKG_STATE_NOT_INSTALLED }, + { NULL, 0 } +}; + + /** * @file lib/package_state.c * @brief Package state handling routines @@ -41,36 +56,23 @@ static int set_new_state(prop_dictionary_t dict, pkg_state_t state) { - const char *pkgname, *state_str; + const struct state *stp; + const char *pkgname; - assert(dict != NULL); - switch (state) { - case XBPS_PKG_STATE_UNPACKED: - state_str = "unpacked"; - break; - case XBPS_PKG_STATE_INSTALLED: - state_str = "installed"; - break; - case XBPS_PKG_STATE_BROKEN: - state_str = "broken"; - break; - case XBPS_PKG_STATE_CONFIG_FILES: - state_str = "config-files"; - break; - case XBPS_PKG_STATE_NOT_INSTALLED: - state_str = "not-installed"; - break; - default: + for (stp = states; stp->string != NULL; stp++) + if (state == stp->number) + break; + + if (stp->string == NULL) return -1; - } - if (!prop_dictionary_set_cstring_nocopy(dict, "state", state_str)) + if (!prop_dictionary_set_cstring_nocopy(dict, "state", stp->string)) return EINVAL; if (prop_dictionary_get_cstring_nocopy(dict, "pkgname", &pkgname)) { xbps_dbg_printf("%s: changed pkg state to '%s'\n", - pkgname, state_str); + pkgname, stp->string); } return 0; @@ -79,28 +81,19 @@ set_new_state(prop_dictionary_t dict, pkg_state_t state) static pkg_state_t get_state(prop_dictionary_t dict) { + const struct state *stp; const char *state_str; - pkg_state_t state = 0; assert(dict != NULL); prop_dictionary_get_cstring_nocopy(dict, "state", &state_str); assert(state_str != NULL); - if (strcmp(state_str, "unpacked") == 0) - state = XBPS_PKG_STATE_UNPACKED; - else if (strcmp(state_str, "installed") == 0) - state = XBPS_PKG_STATE_INSTALLED; - else if (strcmp(state_str, "broken") == 0) - state = XBPS_PKG_STATE_BROKEN; - else if (strcmp(state_str, "config-files") == 0) - state = XBPS_PKG_STATE_CONFIG_FILES; - else if (strcmp(state_str, "not-installed") == 0) - state = XBPS_PKG_STATE_NOT_INSTALLED; - else - return 0; + for (stp = states; stp->string != NULL; stp++) + if (strcmp(state_str, stp->string) == 0) + break; - return state; + return stp->number; } int diff --git a/lib/package_unpack.c b/lib/package_unpack.c index 959aa580..f87c5a71 100644 --- a/lib/package_unpack.c +++ b/lib/package_unpack.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2008-2010 Juan Romero Pardines. + * Copyright (c) 2008-2011 Juan Romero Pardines. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -86,22 +86,90 @@ set_extract_flags(int *flags, bool update) *flags = lflags; } -/* - * TODO: remove printfs and return appropiate errors to be interpreted by - * the consumer. - */ static int -unpack_archive_fini(prop_dictionary_t pkg_repod, - struct archive *ar, - const char *pkgname, - const char *version) +extract_metafile(struct archive *ar, + struct archive_entry *entry, + const char *file, + const char *pkgname, + const char *version, + bool exec, + int flags) +{ + char *buf; + int rv; + + buf = xbps_xasprintf(".%s/metadata/%s/%s", + XBPS_META_PATH, pkgname, file); + if (buf == NULL) + return ENOMEM; + + archive_entry_set_pathname(entry, buf); + free(buf); + if (exec) + archive_entry_set_perm(entry, 0750); + + if (archive_read_extract(ar, entry, flags) != 0) { + if ((rv = archive_errno(ar)) != EEXIST) { + xbps_error_printf("failed to extract metadata file `%s'" + "for `%s-%s': %s\n", file, pkgname, version, + strerror(rv)); + } + } + + return 0; +} + +static int +remove_metafile(const char *file, const char *pkgname, const char *version) +{ + char *buf; + + buf = xbps_xasprintf(".%s/metadata/%s/%s", + XBPS_META_PATH, file, pkgname); + if (buf == NULL) + return ENOMEM; + + if (unlink(buf) == -1) { + if (errno && errno != ENOENT) { + xbps_error_printf("failed to remove metadata file " + "`%s' while unpacking `%s-%s': %s\n", file, + pkgname, version, strerror(errno)); + free(buf); + return errno; + } + } + free(buf); + + return 0; +} + +/* + * Execute the unpack progress function callback if set and its + * private data is also set. It's so sad that + * archive_read_set_progress_callback() from libarchive(3) cannot be used + * here because sometimes it misses some entries by unknown reasons. + */ +#define RUN_PROGRESS_CB() \ +do { \ + if (progress_cb != NULL && xupd != NULL) \ + (*progress_cb)(xupd); \ +} while (0) + +static int +unpack_archive(prop_dictionary_t pkg_repod, + struct archive *ar, + const char *pkgname, + const char *version, + void (*progress_cb)(void *), + struct xbps_unpack_progress_data *xupd) { prop_dictionary_t propsd, filesd, old_filesd; + prop_array_t array; struct archive_entry *entry; - size_t entry_idx = 0; + size_t nmetadata = 0, entry_idx = 0; const char *rootdir, *entry_pname, *transact; char *buf; - int rv, flags, lflags; + int rv, flags; bool preserve, update, replace_files_in_pkg_update; assert(ar != NULL); @@ -109,17 +177,19 @@ unpack_archive_fini(prop_dictionary_t pkg_repod, assert(pkgname != NULL); assert(version != NULL); - preserve = update = replace_files_in_pkg_update = false; + preserve = update = false; rootdir = xbps_get_rootdir(); - flags = xbps_get_flags(); - if (chdir(rootdir) == -1) + if (chdir(rootdir) == -1) { + xbps_error_printf("cannot chdir to rootdir for " + "`%s-%s': %s\n", pkgname, version, strerror(errno)); return errno; + } prop_dictionary_get_bool(pkg_repod, "preserve", &preserve); prop_dictionary_get_cstring_nocopy(pkg_repod, "trans-action", &transact); - assert(transasct != NULL); + assert(transact != NULL); if (strcmp(transact, "update") == 0) update = true; @@ -130,43 +200,28 @@ unpack_archive_fini(prop_dictionary_t pkg_repod, * anymore. */ if (update) { - buf = xbps_xasprintf(".%s/metadata/%s/INSTALL", - XBPS_META_PATH, pkgname); - if (buf == NULL) - return ENOMEM; - - if (unlink(buf) == -1) { - if (errno && errno != ENOENT) { - free(buf); - return errno; - } - } - free(buf); - - buf = xbps_xasprintf(".%s/metadata/%s/REMOVE", - XBPS_META_PATH, pkgname); - if (buf == NULL) - return ENOMEM; - - if (unlink(buf) == -1) { - if (errno && errno != ENOENT) { - free(buf); - return errno; - } - } - free(buf); + if ((rv = remove_metafile("INSTALL", pkgname, version)) != 0) + return rv; + if ((rv = remove_metafile("REMOVE", pkgname, version)) != 0) + return rv; } /* * Process the archive files. */ while (archive_read_next_header(ar, &entry) == ARCHIVE_OK) { entry_pname = archive_entry_pathname(entry); - set_extract_flags(&lflags, update); + set_extract_flags(&flags, update); + if (progress_cb != NULL && xupd != NULL) { + xupd->entry = entry_pname; + xupd->entry_size = archive_entry_size(entry); + xupd->entry_is_metadata = false; + xupd->entry_is_conf = false; + } if (strcmp("./INSTALL", entry_pname) == 0) { /* - * Extract package INSTALL file into destination - * directory and execute the pre install action. + * Extract the INSTALL script first to execute + * the pre install target. */ buf = xbps_xasprintf(".%s/metadata/%s/INSTALL", XBPS_META_PATH, pkgname); @@ -174,85 +229,80 @@ unpack_archive_fini(prop_dictionary_t pkg_repod, rv = ENOMEM; goto out; } - archive_entry_set_pathname(entry, buf); - archive_entry_set_perm(entry, 0750); - if (archive_read_extract(ar, entry, lflags) != 0) { - if ((rv = archive_errno(ar)) != EEXIST) { - free(buf); - goto out; - } + rv = extract_metafile(ar, entry, "INSTALL", + pkgname, version, true, flags); + if (rv != 0) { + free(buf); + goto out; } rv = xbps_file_exec(buf, "pre", pkgname, version, update ? "yes" : "no", NULL); free(buf); if (rv != 0) { - fprintf(stderr, - "%s: preinst action target error %s\n", - pkgname, strerror(errno)); + xbps_error_printf("%s-%s: pre-install script " + "error: %s\n", pkgname, version, + strerror(rv)); goto out; } - /* Pass to the next entry if successful */ - entry_idx++; + nmetadata++; + if (xupd != NULL) { + xupd->entry_is_metadata = true; + xupd->entry_extract_count++; + } + RUN_PROGRESS_CB(); continue; - /* - * Unpack metadata files in final directory. - */ + } else if (strcmp("./REMOVE", entry_pname) == 0) { - buf = xbps_xasprintf(".%s/metadata/%s/REMOVE", - XBPS_META_PATH, pkgname); - if (buf == NULL) { - rv = ENOMEM; + rv = extract_metafile(ar, entry, "REMOVE", + pkgname, version, true, flags); + if (rv != 0) goto out; + + nmetadata++; + if (xupd != NULL) { + xupd->entry_is_metadata = true; + xupd->entry_extract_count++; } - archive_entry_set_pathname(entry, buf); - free(buf); - archive_entry_set_perm(entry, 0750); - if (archive_read_extract(ar, entry, lflags) != 0) { - if ((rv = archive_errno(ar)) != EEXIST) - goto out; - } - /* Pass to next entry if successful */ - entry_idx++; + RUN_PROGRESS_CB(); continue; } else if (strcmp("./files.plist", entry_pname) == 0) { /* - * Now we have a dictionary from the entry - * in memory. Will be written to disk later, when - * all files are extracted. + * Internalize this entry into a prop_dictionary + * to check for obsolete files if updating a package. + * It will be extracted to disk at the end. */ filesd = xbps_read_dict_from_archive_entry(ar, entry); if (filesd == NULL) { rv = errno; goto out; } - /* Pass to next entry */ - entry_idx++; + nmetadata++; + if (xupd != NULL) { + xupd->entry_is_metadata = true; + xupd->entry_extract_count++; + } + RUN_PROGRESS_CB(); continue; } else if (strcmp("./props.plist", entry_pname) == 0) { - buf = xbps_xasprintf(".%s/metadata/%s/%s", - XBPS_META_PATH, pkgname, XBPS_PKGPROPS); - if (buf == NULL) { - rv = ENOMEM; + rv = extract_metafile(ar, entry, XBPS_PKGPROPS, + pkgname, version, false, flags); + if (rv != 0) goto out; - } - archive_entry_set_pathname(entry, buf); - free(buf); - if (archive_read_extract(ar, entry, lflags) != 0) { - rv = archive_errno(ar); - goto out; - } - propsd = - xbps_get_pkg_dict_from_metadata_plist(pkgname, - XBPS_PKGPROPS); + propsd = xbps_get_pkg_dict_from_metadata_plist( + pkgname, XBPS_PKGPROPS); if (propsd == NULL) { rv = errno; goto out; } - /* Pass to next entry if successful */ - entry_idx++; + nmetadata++; + if (xupd != NULL) { + xupd->entry_is_metadata = true; + xupd->entry_extract_count++; + } + RUN_PROGRESS_CB(); continue; } /* @@ -266,12 +316,32 @@ unpack_archive_fini(prop_dictionary_t pkg_repod, * required metadata files weren't found, bail out. * This is not an XBPS binary package. */ - if (entry_idx >= 3) + if (entry_idx >= 3) { + xbps_error_printf("invalid binary pkg archive" + "for `%s-%s'\n", pkgname, version); return ENODEV; + } entry_idx++; continue; } + /* + * Compute total entries in progress data, if set. + * total_entries = metadata + files + conf_files + links. + */ + if (xupd != NULL) { + xupd->entry_total_count = nmetadata; + array = prop_dictionary_get(filesd, "files"); + xupd->entry_total_count += + (ssize_t)prop_array_count(array); + array = prop_dictionary_get(filesd, "conf_files"); + xupd->entry_total_count += + (ssize_t)prop_array_count(array); + array = prop_dictionary_get(filesd, "links"); + xupd->entry_total_count += + (ssize_t)prop_array_count(array); + } + /* * Handle configuration files. Check if current entry is * a configuration file and take action if required. Skip @@ -283,6 +353,9 @@ unpack_archive_fini(prop_dictionary_t pkg_repod, /* error */ goto out; } else if (rv == 1) { + if (xupd != NULL) + xupd->entry_is_conf = true; + rv = xbps_entry_install_conf_file(filesd, entry, entry_pname, pkgname, version); if (rv == -1) { @@ -292,14 +365,15 @@ unpack_archive_fini(prop_dictionary_t pkg_repod, /* * Configuration file should be installed. */ - lflags &= ~ARCHIVE_EXTRACT_NO_OVERWRITE; - lflags &= ~ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER; + flags &= ~ARCHIVE_EXTRACT_NO_OVERWRITE; + flags &= ~ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER; } else { /* * Keep current configuration file * as is now and pass to next entry. */ archive_read_data_skip(ar); + RUN_PROGRESS_CB(); continue; } } @@ -326,31 +400,31 @@ unpack_archive_fini(prop_dictionary_t pkg_repod, "replace-files-in-pkg-update", &replace_files_in_pkg_update); if (replace_files_in_pkg_update) { - lflags &= ~ARCHIVE_EXTRACT_NO_OVERWRITE; - lflags &= ~ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER; + flags &= ~ARCHIVE_EXTRACT_NO_OVERWRITE; + flags &= ~ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER; } /* * Extract entry from archive. */ - if (archive_read_extract(ar, entry, lflags) != 0) { + if (archive_read_extract(ar, entry, flags) != 0) { rv = archive_errno(ar); - if (rv && rv != EEXIST) { - fprintf(stderr, "ERROR: extracting `%s' " - "(%s) ...exiting!\n", entry_pname, - archive_error_string(ar)); + if (rv != EEXIST) { + xbps_error_printf("failed to extract `%s' " + "from `%s-%s': %s\n", entry_pname, + pkgname, version, strerror(rv)); goto out; - } else if (rv == EEXIST) { - if (flags & XBPS_FLAG_VERBOSE) { - fprintf(stderr, - "WARNING: ignoring existent " - "entry: %s\n", entry_pname); - } + } else { + xbps_warn_printf("ignoring existing " + "entry: %s\n", entry_pname); + RUN_PROGRESS_CB(); continue; } } - if (flags & XBPS_FLAG_VERBOSE) - printf(" %s\n", entry_pname); + if (xupd != NULL) + xupd->entry_extract_count++; + + RUN_PROGRESS_CB(); } if ((rv = archive_errno(ar)) == 0) { @@ -390,8 +464,11 @@ unpack_archive_fini(prop_dictionary_t pkg_repod, * is reachable. */ if (!prop_dictionary_externalize_to_zfile(filesd, buf)) { - free(buf); rv = errno; + xbps_error_printf("failed to extract metadata %s file" + "for `%s-%s': %s\n", XBPS_PKGFILES, pkgname, + version, strerror(rv)); + free(buf); goto out; } free(buf); @@ -405,14 +482,17 @@ out: return rv; } +#undef RUN_PROGRESS_CB int -xbps_unpack_binary_pkg(prop_dictionary_t pkg_repod) +xbps_unpack_binary_pkg(prop_dictionary_t pkg_repod, + void (*progress_cb)(void *), + struct xbps_unpack_progress_data *xupd) { + struct archive *ar; const char *pkgname, *version, *repoloc; - struct archive *ar = NULL; - char *binfile = NULL; - int pkg_fd, rv = 0; + char *bpkg; + int rv = 0; assert(pkg_repod != NULL); @@ -420,50 +500,61 @@ xbps_unpack_binary_pkg(prop_dictionary_t pkg_repod) prop_dictionary_get_cstring_nocopy(pkg_repod, "version", &version); prop_dictionary_get_cstring_nocopy(pkg_repod, "repository", &repoloc); - binfile = xbps_get_binpkg_repo_uri(pkg_repod, repoloc); - if (binfile == NULL) - return EINVAL; - - if ((pkg_fd = open(binfile, O_RDONLY)) == -1) { - rv = errno; - xbps_dbg_printf("cannot open '%s' for unpacking %s\n", - binfile, strerror(errno)); - free(binfile); - goto out; + bpkg = xbps_get_binpkg_repo_uri(pkg_repod, repoloc); + if (bpkg == NULL) { + xbps_error_printf("cannot determine binary pkg file " + "for `%s-%s': %s\n", pkgname, version, strerror(errno)); + return errno; } - free(binfile); ar = archive_read_new(); if (ar == NULL) { - rv = errno; + rv = ENOMEM; goto out; } - /* * Enable support for tar format and all compression methods. */ archive_read_support_compression_all(ar); archive_read_support_format_tar(ar); - if (archive_read_open_fd(ar, pkg_fd, - ARCHIVE_READ_BLOCKSIZE) != 0) { - rv = errno; + if (archive_read_open_filename(ar, bpkg, ARCHIVE_READ_BLOCKSIZE) != 0) { + rv = archive_errno(ar); + xbps_error_printf("failed to open `%s' binpkg: %s\n", + bpkg, strerror(rv)); goto out; } - - if ((rv = unpack_archive_fini(pkg_repod, ar, pkgname, version)) != 0) + /* + * Set extract progress callback if specified. + */ + if (progress_cb != NULL && xupd != NULL) { + xupd->entry_extract_count = 0; + xupd->entry_total_count = 0; + } + /* + * Extract archive files. + */ + rv = unpack_archive(pkg_repod, ar, pkgname, version, + progress_cb, xupd); + if (rv != 0) { + xbps_error_printf("failed to unpack `%s' binpkg: %s\n", + bpkg, strerror(rv)); goto out; + } /* * Set package state to unpacked. */ rv = xbps_set_pkg_state_installed(pkgname, XBPS_PKG_STATE_UNPACKED); - + if (rv != 0) { + xbps_error_printf("failed to set `%s-%s' to unpacked " + "state: %s\n", pkgname, version, strerror(rv)); + } out: + if (bpkg) + free(bpkg); if (ar) archive_read_finish(ar); - if (pkg_fd != -1) - (void)close(pkg_fd); return rv; } diff --git a/lib/pkgmatch.c b/lib/pkgmatch.c index 62ea455a..c8aa9fde 100644 --- a/lib/pkgmatch.c +++ b/lib/pkgmatch.c @@ -117,12 +117,10 @@ int xbps_pkgpattern_match(const char *instpkg, char *pattern) { const char *fname = instpkg; - char *basefname, condchar = '\0', *condition; + char *basefname = NULL, condchar = '\0', *condition; size_t len = 0; int rv = 0; - memset(&basefname, 0, sizeof(basefname)); - /* Check for a full match with strcmp, otherwise try csh_match() */ if (strcmp(instpkg, pattern) == 0) return 1; diff --git a/lib/repository_sync_index.c b/lib/repository_sync_index.c index 20f1d43e..0a253384 100644 --- a/lib/repository_sync_index.c +++ b/lib/repository_sync_index.c @@ -82,7 +82,9 @@ xbps_get_remote_repo_string(const char *uri) * size and/or mtime match) and 1 if downloaded successfully. */ int -xbps_repository_sync_pkg_index(const char *uri) +xbps_repository_sync_pkg_index(const char *uri, + void (*progress_cb)(void *), + struct xbps_fetch_progress_data *xfpd) { struct url *url = NULL; struct utsname un; @@ -160,8 +162,9 @@ xbps_repository_sync_pkg_index(const char *uri) /* * Download pkg-index.plist file from repository. */ - if ((rv = xbps_fetch_file(rpidx, fetch_outputdir, - true, NULL)) == -1) { + rv = xbps_fetch_file(rpidx, fetch_outputdir, true, NULL, + progress_cb, xfpd); + if (rv == -1) { (void)remove(tmp_metafile); goto out; } diff --git a/prog.mk b/prog.mk index 238f04ed..c77d99d2 100644 --- a/prog.mk +++ b/prog.mk @@ -34,11 +34,11 @@ endif $(BIN).static: $(OBJS) @printf " [CCLD]\t\t$@\n" - ${SILENT}$(CC) -static $^ $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) \ - $(STATIC_LIBS) -o $@ + ${SILENT}$(CC) -static $^ $(CPPFLAGS) -L$(TOPDIR)/lib \ + $(CFLAGS) $(LDFLAGS) $(STATIC_LIBS) -o $@ $(BIN): $(OBJS) @printf " [CCLD]\t\t$@\n" - ${SILENT}$(CC) $^ $(CPPFLAGS) $(CFLAGS) $(PROG_CFLAGS) \ - $(LDFLAGS) $(PROG_LDFLAGS) -o $@ + ${SILENT}$(CC) $^ $(CPPFLAGS) -L$(TOPDIR)/lib $(CFLAGS) \ + $(PROG_CFLAGS) $(LDFLAGS) $(PROG_LDFLAGS) -o $@