summaryrefslogtreecommitdiff
path: root/src/pacman/callback.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/pacman/callback.c')
-rw-r--r--src/pacman/callback.c627
1 files changed, 627 insertions, 0 deletions
diff --git a/src/pacman/callback.c b/src/pacman/callback.c
new file mode 100644
index 00000000..d86c7d9c
--- /dev/null
+++ b/src/pacman/callback.c
@@ -0,0 +1,627 @@
+/*
+ * callback.c
+ *
+ * Copyright (c) 2002-2007 by Judd Vinet <jvinet@zeroflux.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <wchar.h>
+#include <math.h>
+
+#include <alpm.h>
+
+/* pacman */
+#include "callback.h"
+#include "util.h"
+#include "log.h"
+#include "conf.h"
+
+/* TODO this should not have to be defined twice- trans.c & log.c */
+#define LOG_STR_LEN 256
+#define FILENAME_TRIM_LEN 23
+
+extern config_t *config;
+
+/* download progress bar */
+static float rate_last;
+static int xfered_last;
+static struct timeval initial_time;
+
+/* transaction progress bar ? */
+static int prevpercent=0; /* for less progressbar output */
+
+/* refactored from cb_trans_progress */
+static void fill_progress(const int percent, const int proglen)
+{
+ const unsigned short chomp = alpm_option_get_chomp();
+ const unsigned int hashlen = proglen - 8;
+ const unsigned int hash = percent * hashlen / 100;
+ static unsigned int lasthash = 0, mouth = 0;
+ unsigned int i;
+
+ /* printf("\ndebug: proglen: %i\n", proglen); DEBUG*/
+
+ if(percent == 0) {
+ lasthash = 0;
+ mouth = 0;
+ }
+
+ /* magic numbers, how I loathe thee */
+ if(proglen > 8) {
+ printf(" [");
+ for(i = hashlen; i > 1; --i) {
+ /* if special progress bar enabled */
+ if(chomp) {
+ if(i > hashlen - hash) {
+ printf("-");
+ } else if(i == hashlen - hash) {
+ if(lasthash == hash) {
+ if(mouth) {
+ printf("\033[1;33mC\033[m");
+ } else {
+ printf("\033[1;33mc\033[m");
+ }
+ } else {
+ lasthash = hash;
+ mouth = mouth == 1 ? 0 : 1;
+ if(mouth) {
+ printf("\033[1;33mC\033[m");
+ } else {
+ printf("\033[1;33mc\033[m");
+ }
+ }
+ } else if(i%3 == 0) {
+ printf("\033[0;37mo\033[m");
+ } else {
+ printf("\033[0;37m \033[m");
+ }
+ } /* else regular progress bar */
+ else if(i > hashlen - hash) {
+ printf("#");
+ } else {
+ printf("-");
+ }
+ }
+ printf("]");
+ }
+ /* print percent after progress bar */
+ if(proglen > 5) {
+ printf(" %3d%%", percent);
+ }
+
+ if(percent == 100) {
+ printf("\n");
+ } else {
+ printf("\r");
+ }
+ fflush(stdout);
+}
+
+
+
+/* callback to handle messages/notifications from libalpm transactions */
+void cb_trans_evt(pmtransevt_t event, void *data1, void *data2)
+{
+ char str[LOG_STR_LEN] = "";
+
+ switch(event) {
+ case PM_TRANS_EVT_CHECKDEPS_START:
+ MSG(NL, _("checking dependencies... "));
+ break;
+ case PM_TRANS_EVT_FILECONFLICTS_START:
+ if(config->noprogressbar) {
+ MSG(NL, _("checking for file conflicts... "));
+ }
+ break;
+ case PM_TRANS_EVT_CLEANUP_START:
+ MSG(NL, _("cleaning up... "));
+ break;
+ case PM_TRANS_EVT_RESOLVEDEPS_START:
+ MSG(NL, _("resolving dependencies... "));
+ break;
+ case PM_TRANS_EVT_INTERCONFLICTS_START:
+ MSG(NL, _("looking for inter-conflicts... "));
+ break;
+ case PM_TRANS_EVT_FILECONFLICTS_DONE:
+ if(config->noprogressbar) {
+ MSG(CL, _("done.\n"));
+ }
+ break;
+ case PM_TRANS_EVT_CHECKDEPS_DONE:
+ case PM_TRANS_EVT_CLEANUP_DONE:
+ case PM_TRANS_EVT_RESOLVEDEPS_DONE:
+ case PM_TRANS_EVT_INTERCONFLICTS_DONE:
+ MSG(CL, _("done.\n"));
+ break;
+ case PM_TRANS_EVT_EXTRACT_DONE:
+ break;
+ case PM_TRANS_EVT_ADD_START:
+ if(config->noprogressbar) {
+ MSG(NL, _("installing %s... "), alpm_pkg_get_name(data1));
+ }
+ break;
+ case PM_TRANS_EVT_ADD_DONE:
+ if(config->noprogressbar) {
+ MSG(CL, _("done.\n"));
+ }
+ snprintf(str, LOG_STR_LEN, _("installed %s (%s)"),
+ alpm_pkg_get_name(data1),
+ alpm_pkg_get_version(data1));
+ alpm_logaction(str);
+ break;
+ case PM_TRANS_EVT_REMOVE_START:
+ if(config->noprogressbar) {
+ MSG(NL, _("removing %s... "), alpm_pkg_get_name(data1));
+ }
+ break;
+ case PM_TRANS_EVT_REMOVE_DONE:
+ if(config->noprogressbar) {
+ MSG(CL, _("done.\n"));
+ }
+ snprintf(str, LOG_STR_LEN, _("removed %s (%s)"),
+ alpm_pkg_get_name(data1),
+ alpm_pkg_get_version(data1));
+ alpm_logaction(str);
+ break;
+ case PM_TRANS_EVT_UPGRADE_START:
+ if(config->noprogressbar) {
+ MSG(NL, _("upgrading %s... "), alpm_pkg_get_name(data1));
+ }
+ break;
+ case PM_TRANS_EVT_UPGRADE_DONE:
+ if(config->noprogressbar) {
+ MSG(CL, _("done.\n"));
+ }
+ snprintf(str, LOG_STR_LEN, _("upgraded %s (%s -> %s)"),
+ (char *)alpm_pkg_get_name(data1),
+ (char *)alpm_pkg_get_version(data2),
+ (char *)alpm_pkg_get_version(data1));
+ alpm_logaction(str);
+ break;
+ case PM_TRANS_EVT_INTEGRITY_START:
+ MSG(NL, _("checking package integrity... "));
+ break;
+ case PM_TRANS_EVT_INTEGRITY_DONE:
+ MSG(CL, _("done.\n"));
+ break;
+ case PM_TRANS_EVT_SCRIPTLET_INFO:
+ MSG(NL, "%s\n", (char*)data1);
+ break;
+ case PM_TRANS_EVT_SCRIPTLET_START:
+ MSG(NL, (char*)data1);
+ MSG(CL, "...");
+ break;
+ case PM_TRANS_EVT_SCRIPTLET_DONE:
+ if(!(long)data1) {
+ MSG(CL, _("done.\n"));
+ } else {
+ MSG(CL, _("failed.\n"));
+ }
+ break;
+ case PM_TRANS_EVT_PRINTURI:
+ MSG(NL, "%s/%s\n", (char*)data1, (char*)data2);
+ break;
+ case PM_TRANS_EVT_RETRIEVE_START:
+ MSG(NL, _(":: Retrieving packages from %s...\n"), (char*)data1);
+ fflush(stdout);
+ break;
+ }
+}
+
+/* callback to handle questions from libalpm transactions (yes/no) */
+void cb_trans_conv(pmtransconv_t event, void *data1, void *data2,
+ void *data3, int *response)
+{
+ char str[LOG_STR_LEN] = "";
+
+ switch(event) {
+ case PM_TRANS_CONV_INSTALL_IGNOREPKG:
+ if(config->noask) {
+ if(config->ask & PM_TRANS_CONV_INSTALL_IGNOREPKG) {
+ *response = 1;
+ } else {
+ *response = 0;
+ }
+ } else if(data2) {
+ /* TODO we take this route based on data2 being not null? WTF */
+ snprintf(str, LOG_STR_LEN, _(":: %1$s requires %2$s from IgnorePkg. Install %2$s? [Y/n] "),
+ alpm_pkg_get_name(data1),
+ alpm_pkg_get_name(data2));
+ *response = yesno(str);
+ } else {
+ snprintf(str, LOG_STR_LEN, _(":: %s is in IgnorePkg. Install anyway? [Y/n] "),
+ alpm_pkg_get_name(data1));
+ *response = yesno(str);
+ }
+ break;
+ case PM_TRANS_CONV_REMOVE_HOLDPKG:
+ if(config->noask) {
+ if(config->ask & PM_TRANS_CONV_REMOVE_HOLDPKG) {
+ *response = 1;
+ } else {
+ *response = 0;
+ }
+ } else {
+ snprintf(str, LOG_STR_LEN, _(":: %s is designated as a HoldPkg. Remove anyway? [Y/n] "),
+ alpm_pkg_get_name(data1));
+ *response = yesno(str);
+ }
+ break;
+ case PM_TRANS_CONV_REPLACE_PKG:
+ if(config->noask) {
+ if(config->ask & PM_TRANS_CONV_REPLACE_PKG) {
+ *response = 1;
+ } else {
+ *response = 0;
+ }
+ } else {
+ snprintf(str, LOG_STR_LEN, _(":: Replace %s with %s/%s? [Y/n] "),
+ alpm_pkg_get_name(data1),
+ (char *)data3,
+ alpm_pkg_get_name(data2));
+ *response = yesno(str);
+ }
+ break;
+ case PM_TRANS_CONV_CONFLICT_PKG:
+ if(config->noask) {
+ if(config->ask & PM_TRANS_CONV_CONFLICT_PKG) {
+ *response = 1;
+ } else {
+ *response = 0;
+ }
+ } else {
+ snprintf(str, LOG_STR_LEN, _(":: %s conflicts with %s. Remove %s? [Y/n] "),
+ (char *)data1,
+ (char *)data2,
+ (char *)data2);
+ *response = yesno(str);
+ }
+ break;
+ case PM_TRANS_CONV_LOCAL_NEWER:
+ if(config->noask) {
+ if(config->ask & PM_TRANS_CONV_LOCAL_NEWER) {
+ *response = 1;
+ } else {
+ *response = 0;
+ }
+ } else {
+ if(!config->op_s_downloadonly) {
+ snprintf(str, LOG_STR_LEN, _(":: %s-%s: local version is newer. Upgrade anyway? [Y/n] "),
+ alpm_pkg_get_name(data1),
+ alpm_pkg_get_version(data1));
+ *response = yesno(str);
+ } else {
+ *response = 1;
+ }
+ }
+ break;
+ case PM_TRANS_CONV_LOCAL_UPTODATE:
+ if(config->noask) {
+ if(config->ask & PM_TRANS_CONV_LOCAL_UPTODATE) {
+ *response = 1;
+ } else {
+ *response = 0;
+ }
+ } else {
+ if(!config->op_s_downloadonly) {
+ snprintf(str, LOG_STR_LEN, _(":: %s-%s: local version is up to date. Upgrade anyway? [Y/n] "),
+ alpm_pkg_get_name(data1),
+ alpm_pkg_get_version(data1));
+ *response = yesno(str);
+ } else {
+ *response = 1;
+ }
+ }
+ break;
+ case PM_TRANS_CONV_CORRUPTED_PKG:
+ if(config->noask) {
+ if(config->ask & PM_TRANS_CONV_CORRUPTED_PKG) {
+ *response = 1;
+ } else {
+ *response = 0;
+ }
+ } else {
+ if(!config->noconfirm) {
+ snprintf(str, LOG_STR_LEN, _(":: Archive %s is corrupted. Do you want to delete it? [Y/n] "),
+ (char *)data1);
+ *response = yesno(str);
+ } else {
+ *response = 1;
+ }
+ }
+ break;
+ }
+}
+
+/* callback to handle display of transaction progress */
+void cb_trans_progress(pmtransprog_t event, const char *pkgname, int percent,
+ int howmany, int remain)
+{
+ float timediff;
+
+ /* size of line to allocate for text printing (e.g. not progressbar) */
+ const int infolen = 50;
+ int tmp, digits, oprlen, textlen, pkglen;
+ char *opr = NULL;
+ wchar_t *wcopr = NULL;
+
+ if(config->noprogressbar) {
+ return;
+ }
+
+ /* XXX: big fat hack: due to the fact that we switch out printf/pm_fprintf,
+ * not everything honors our 'neednl' newline hackery. This forces a newline
+ * if we need one before drawing the progress bar */
+ MSG(NL,NULL);
+
+ if(percent == 0) {
+ set_output_padding(1); /* turn on output padding with ' ' */
+ timediff = get_update_timediff(1);
+ } else {
+ timediff = get_update_timediff(0);
+ }
+
+ if(percent > 0 && percent < 100 && !timediff) {
+ /* only update the progress bar when
+ * a) we first start
+ * b) we end the progress
+ * c) it has been long enough since the last call
+ */
+ return;
+ }
+
+ /* if no pkgname, percent is too high or unchanged, then return */
+ if(!pkgname || percent == prevpercent) {
+ return;
+ }
+
+ prevpercent=percent;
+ /* set text of message to display */
+ switch (event) {
+ case PM_TRANS_PROGRESS_ADD_START:
+ opr = _("installing");
+ break;
+ case PM_TRANS_PROGRESS_UPGRADE_START:
+ opr = _("upgrading");
+ break;
+ case PM_TRANS_PROGRESS_REMOVE_START:
+ opr = _("removing");
+ break;
+ case PM_TRANS_PROGRESS_CONFLICTS_START:
+ opr = _("checking for file conflicts");
+ break;
+ }
+ /* convert above strings to wide chars */
+ oprlen = strlen(opr);
+ wcopr = (wchar_t*)calloc(oprlen, sizeof(wchar_t));
+ if(!wcopr) {
+ fprintf(stderr, "malloc failure: could not allocate %d bytes\n",
+ strlen(opr) * sizeof(wchar_t));
+ }
+ oprlen = mbstowcs(wcopr, opr, oprlen);
+
+ /* find # of digits in package counts to scale output */
+ digits = 1;
+ tmp = howmany;
+ while((tmp /= 10)) {
+ ++digits;
+ }
+
+ /* determine room left for non-digits text [not ( 1/12) part] */
+ textlen = infolen - 3 - (2 * digits);
+ /* room left for package name */
+ pkglen = textlen - oprlen - 1;
+
+ switch (event) {
+ case PM_TRANS_PROGRESS_ADD_START:
+ case PM_TRANS_PROGRESS_UPGRADE_START:
+ case PM_TRANS_PROGRESS_REMOVE_START:
+ printf("(%2$*1$d/%3$*1$d) %4$s %6$-*5$.*5$s", digits, remain, howmany,
+ opr, pkglen, pkgname);
+ break;
+ case PM_TRANS_PROGRESS_CONFLICTS_START:
+ printf("(%2$*1$d/%3$*1$d) %5$-*4$s", digits, remain, howmany,
+ textlen, opr);
+ break;
+ }
+
+ free(wcopr);
+
+ /* call refactored fill progress function */
+ fill_progress(percent, getcols() - infolen);
+
+ if(percent >= 100) {
+ set_output_padding(0); /* restore padding */
+ }
+}
+
+/* callback to handle display of download progress */
+void cb_dl_progress(const char *filename, int xfered, int total)
+{
+ const int infolen = 50;
+ char *fname, *p;
+
+ float rate = 0.0, timediff = 0.0, f_xfered = 0.0;
+ unsigned int eta_h = 0, eta_m = 0, eta_s = 0;
+ int percent;
+ char rate_size = 'K', xfered_size = 'K';
+
+ if(config->noprogressbar) {
+ return;
+ }
+
+ /* XXX: big fat hack: due to the fact that we switch out printf/pm_fprintf,
+ * not everything honors our 'neednl' newline hackery. This forces a newline
+ * if we need one before drawing the progress bar */
+ MSG(NL,NULL);
+
+ /* this is basically a switch on xferred: 0, total, and anything else */
+ if(xfered == 0) {
+ /* set default starting values */
+ gettimeofday(&initial_time, NULL);
+ xfered_last = 0;
+ rate_last = 0.0;
+ timediff = get_update_timediff(1);
+ rate = 0.0;
+ eta_s = 0;
+ set_output_padding(1); /* we need padding from pm_fprintf output */
+ } else if(xfered == total) {
+ /* compute final values */
+ struct timeval current_time;
+ float diff_sec, diff_usec;
+
+ gettimeofday(&current_time, NULL);
+ diff_sec = current_time.tv_sec - initial_time.tv_sec;
+ diff_usec = current_time.tv_usec - initial_time.tv_usec;
+ timediff = diff_sec + (diff_usec / 1000000.0);
+ rate = (float)total / (timediff * 1024.0);
+
+ /* round elapsed time to the nearest second */
+ eta_s = (int)floorf(timediff + 0.5);
+
+ set_output_padding(0); /* shut off padding */
+ } else {
+ /* compute current average values */
+ timediff = get_update_timediff(0);
+
+ if(timediff < UPDATE_SPEED_SEC) {
+ /* return if the calling interval was too short */
+ return;
+ }
+ rate = (float)(xfered - xfered_last) / (timediff * 1024.0);
+ /* average rate to reduce jumpiness */
+ rate = (float)(rate + 2*rate_last) / 3;
+ eta_s = (unsigned int)(total - xfered) / (rate * 1024.0);
+ rate_last = rate;
+ xfered_last = xfered;
+ }
+
+ percent = (int)((float)xfered) / ((float)total) * 100;
+
+ /* fix up time for display */
+ eta_h = eta_s / 3600;
+ eta_s -= eta_h * 3600;
+ eta_m = eta_s / 60;
+ eta_s -= eta_m * 60;
+
+ fname = strdup(filename);
+ /* strip extension if it's there
+ * NOTE: in the case of package files, only the pkgname is sent now */
+ if((p = strstr(fname, PM_EXT_PKG)) || (p = strstr(fname, PM_EXT_DB))) {
+ *p = '\0';
+ }
+ if(strlen(fname) > FILENAME_TRIM_LEN) {
+ strcpy(fname + FILENAME_TRIM_LEN -3,"...");
+ }
+
+ /* Awesome formatting for progress bar. We need a mess of Kb->Mb->Gb stuff
+ * here. We'll use limit of 2048 for each until we get some empirical */
+ /* rate_size = 'K'; was set above */
+ if(rate > 2048.0) {
+ rate /= 1024.0;
+ rate_size = 'M';
+ if(rate > 2048.0) {
+ rate /= 1024.0;
+ rate_size = 'G';
+ /* we should not go higher than this for a few years (9999.9 Gb/s?)*/
+ }
+ }
+
+ f_xfered = (float) xfered / 1024.0; /* convert to K by default */
+ /* xfered_size = 'K'; was set above */
+ if(f_xfered > 2048.0) {
+ f_xfered /= 1024.0;
+ xfered_size = 'M';
+ if(f_xfered > 2048.0) {
+ f_xfered /= 1024.0;
+ xfered_size = 'G';
+ /* I should seriously hope that archlinux packages never break
+ * the 9999.9GB mark... we'd have more serious problems than the progress
+ * bar in pacman */
+ }
+ }
+
+ printf(" %-*s %6.1f%c %#6.1f%c/s %02u:%02u:%02u", FILENAME_TRIM_LEN, fname,
+ f_xfered, xfered_size, rate, rate_size, eta_h, eta_m, eta_s);
+
+ free(fname);
+
+ fill_progress(percent, getcols() - infolen);
+ return;
+}
+
+/* Callback to handle notifications from the library */
+void cb_log(unsigned short level, char *msg)
+{
+ char str[LOG_STR_LEN] = "";
+
+ if(!strlen(msg)) {
+ return;
+ }
+
+ switch(level) {
+ case PM_LOG_DEBUG:
+ sprintf(str, _("debug"));
+ break;
+ case PM_LOG_ERROR:
+ sprintf(str, _("error"));
+ break;
+ case PM_LOG_WARNING:
+ sprintf(str, _("warning"));
+ break;
+ case PM_LOG_FUNCTION:
+ /* TODO we should increase the indent level when this occurs so we can see
+ * program flow easier. It'll be fun
+ */
+ sprintf(str, _("function"));
+ break;
+ default:
+ sprintf(str, "???");
+ break;
+ }
+
+#ifdef PACMAN_DEBUG
+ /* If debug is on, we'll timestamp the output */
+ if(alpm_option_get_logmask() & PM_LOG_DEBUG) {
+ time_t t;
+ struct tm *tmp;
+ char timestr[10] = {0};
+
+ t = time(NULL);
+ tmp = localtime(&t);
+ strftime(timestr, 9, "%H:%M:%S", tmp);
+ timestr[8] = '\0';
+
+ MSG(NL, "[%s] %s: %s", timestr, str, msg);
+ } else {
+ MSG(NL, "%s: %s", str, msg);
+ }
+#else
+ MSG(NL, "%s: %s", str, msg);
+#endif
+}
+
+/* vim: set ts=2 sw=2 noet: */