[pacman-dev] [PATCH] Reimplement TotalDownload functionality

Dan McGee dan at archlinux.org
Mon Jun 2 00:14:11 EDT 2008


Add a new totaldlcb callback function to libalpm and make pacman utilize it
when the TotalDownload option is enabled. This callback function is pretty
simple- it is meant to be called once at the beginning of a "list download"
action, and once at the end (with value 0 to indicate the list has been
finished). The frontend is responsible for keeping track of adding
individual file download amounts to the total xfered amount in order to
display some sort of overall progress.

Signed-off-by: Dan McGee <dan at archlinux.org>
---
 lib/libalpm/alpm.h    |    6 ++-
 lib/libalpm/handle.c  |   18 +++++++++++
 lib/libalpm/handle.h  |    1 +
 lib/libalpm/sync.c    |   20 ++++++++++++-
 src/pacman/callback.c |   76 +++++++++++++++++++++++++++++++++++++-----------
 src/pacman/callback.h |    2 +
 src/pacman/pacman.c   |    5 +++
 7 files changed, 107 insertions(+), 21 deletions(-)

diff --git a/lib/libalpm/alpm.h b/lib/libalpm/alpm.h
index fd3be0d..9ed2c67 100644
--- a/lib/libalpm/alpm.h
+++ b/lib/libalpm/alpm.h
@@ -82,19 +82,21 @@ int alpm_logaction(char *fmt, ...);
 
 typedef void (*alpm_cb_download)(const char *filename,
 		off_t xfered, off_t total);
+typedef void (*alpm_cb_totaldl)(off_t total);
 
 /*
  * Options
  */
 
-#define PM_DLFNM_LEN 22
-
 alpm_cb_log alpm_option_get_logcb();
 void alpm_option_set_logcb(alpm_cb_log cb);
 
 alpm_cb_download alpm_option_get_dlcb();
 void alpm_option_set_dlcb(alpm_cb_download cb);
 
+alpm_cb_totaldl alpm_option_get_totaldlcb();
+void alpm_option_set_totaldlcb(alpm_cb_totaldl cb);
+
 const char *alpm_option_get_root();
 int alpm_option_set_root(const char *root);
 
diff --git a/lib/libalpm/handle.c b/lib/libalpm/handle.c
index c01dd55..af1cc78 100644
--- a/lib/libalpm/handle.c
+++ b/lib/libalpm/handle.c
@@ -115,6 +115,15 @@ alpm_cb_download SYMEXPORT alpm_option_get_dlcb()
 	return handle->dlcb;
 }
 
+alpm_cb_totaldl SYMEXPORT alpm_option_get_totaldlcb()
+{
+	if (handle == NULL) {
+		pm_errno = PM_ERR_HANDLE_NULL;
+		return NULL;
+	}
+	return handle->totaldlcb;
+}
+
 const char SYMEXPORT *alpm_option_get_root()
 {
 	if (handle == NULL) {
@@ -268,6 +277,15 @@ void SYMEXPORT alpm_option_set_dlcb(alpm_cb_download cb)
 	handle->dlcb = cb;
 }
 
+void SYMEXPORT alpm_option_set_totaldlcb(alpm_cb_totaldl cb)
+{
+	if (handle == NULL) {
+		pm_errno = PM_ERR_HANDLE_NULL;
+		return;
+	}
+	handle->totaldlcb = cb;
+}
+
 int SYMEXPORT alpm_option_set_root(const char *root)
 {
 	struct stat st;
diff --git a/lib/libalpm/handle.h b/lib/libalpm/handle.h
index 9c537b1..bec0a6f 100644
--- a/lib/libalpm/handle.h
+++ b/lib/libalpm/handle.h
@@ -39,6 +39,7 @@ typedef struct _pmhandle_t {
 	/* callback functions */
 	alpm_cb_log logcb;      /* Log callback function */
 	alpm_cb_download dlcb;  /* Download callback function */
+	alpm_cb_totaldl totaldlcb;  /* Total download callback function */
 
 	/* filesystem paths */
 	char *root;              /* Root path, default '/' */
diff --git a/lib/libalpm/sync.c b/lib/libalpm/sync.c
index 2dad8bf..3dc54d0 100644
--- a/lib/libalpm/sync.c
+++ b/lib/libalpm/sync.c
@@ -811,12 +811,25 @@ int _alpm_sync_commit(pmtrans_t *trans, pmdb_t *db_local, alpm_list_t **data)
 
 	ALPM_LOG_FUNC;
 
-	ASSERT(db_local != NULL, RET_ERR(PM_ERR_DB_NULL, -1));
 	ASSERT(trans != NULL, RET_ERR(PM_ERR_TRANS_NULL, -1));
 
 	cachedir = _alpm_filecache_setup();
 	trans->state = STATE_DOWNLOADING;
 
+	/* Total progress - figure out the total download size if required to
+	 * pass to the callback. This function is called once, and it is up to the
+	 * frontend to compute incremental progress. */
+	if(handle->totaldlcb) {
+		off_t total_size = (off_t)0;
+		/* sum up the download size for each package and store total */
+		for(i = trans->packages; i; i = i->next) {
+			pmsyncpkg_t *sync = i->data;
+			pmpkg_t *spkg = sync->pkg;
+			total_size += spkg->download_size;
+		}
+		handle->totaldlcb(total_size);
+	}
+
 	/* group sync records by repository and download */
 	for(i = handle->dbs_sync; i; i = i->next) {
 		pmdb_t *current = i->data;
@@ -877,6 +890,11 @@ int _alpm_sync_commit(pmtrans_t *trans, pmdb_t *db_local, alpm_list_t **data)
 		return(0);
 	}
 
+	/* clear out value to let callback know we are done */
+	if(handle->totaldlcb) {
+		handle->totaldlcb(0);
+	}
+
 	if(handle->usedelta) {
 		int ret = 0;
 
diff --git a/src/pacman/callback.c b/src/pacman/callback.c
index 1942aef..ff125c3 100644
--- a/src/pacman/callback.c
+++ b/src/pacman/callback.c
@@ -38,6 +38,8 @@
 /* download progress bar */
 static float rate_last;
 static off_t xfered_last;
+static off_t list_xfered = 0.0;
+static off_t list_total = 0.0;
 static struct timeval initial_time;
 
 /* transaction progress bar */
@@ -84,14 +86,15 @@ static float get_update_timediff(int first_call)
 }
 
 /* refactored from cb_trans_progress */
-static void fill_progress(const int percent, const int proglen)
+static void fill_progress(const int bar_percent, const int disp_percent,
+		const int proglen)
 {
 	const unsigned int hashlen = proglen - 8;
-	const unsigned int hash = percent * hashlen / 100;
+	const unsigned int hash = bar_percent * hashlen / 100;
 	static unsigned int lasthash = 0, mouth = 0;
 	unsigned int i;
 
-	if(percent == 0) {
+	if(bar_percent == 0) {
 		lasthash = 0;
 		mouth = 0;
 	}
@@ -136,10 +139,12 @@ static void fill_progress(const int percent, const int proglen)
 	}
 	/* print percent after progress bar */
 	if(proglen > 5) {
-		printf(" %3d%%", percent);
+		/* use disp_percent if it is not 0, else show bar_percent */
+		int p = disp_percent ? disp_percent : bar_percent;
+		printf(" %3d%%", p);
 	}
 
-	if(percent == 100) {
+	if(bar_percent == 100) {
 		printf("\n");
 	} else {
 		printf("\r");
@@ -395,7 +400,7 @@ void cb_trans_progress(pmtransprog_t event, const char *pkgname, int percent,
 	free(wcstr);
 
 	/* call refactored fill progress function */
-	fill_progress(percent, getcols() - infolen);
+	fill_progress(percent, percent, getcols() - infolen);
 
 	if(percent == 100) {
 		alpm_list_t *i = NULL;
@@ -410,8 +415,19 @@ void cb_trans_progress(pmtransprog_t event, const char *pkgname, int percent,
 	}
 }
 
+/* callback to handle receipt of total download value */
+void cb_dl_total(off_t total)
+{
+	list_total = total;
+	/* if we get a 0 value, it means this list has finished downloading,
+	 * so clear out our list_xfered as well */
+	if(total == 0) {
+		list_xfered = 0;
+	}
+}
+
 /* callback to handle display of download progress */
-void cb_dl_progress(const char *filename, off_t xfered, off_t total)
+void cb_dl_progress(const char *filename, off_t file_xfered, off_t file_total)
 {
 	const int infolen = 50;
 	const int filenamelen = infolen - 27;
@@ -420,24 +436,38 @@ void cb_dl_progress(const char *filename, off_t xfered, off_t total)
 	int len, wclen, wcwid, padwid;
 	wchar_t *wcfname;
 
+	off_t xfered, total;
 	float rate = 0.0, timediff = 0.0, f_xfered = 0.0;
 	unsigned int eta_h = 0, eta_m = 0, eta_s = 0;
-	int percent;
+	int file_percent = 0, total_percent = 0;
 	char rate_size = 'K', xfered_size = 'K';
 
 	if(config->noprogressbar) {
 		return;
 	}
 
+	/* only use TotalDownload if enabled and we have a callback value */
+	if(config->totaldownload && list_total) {
+		xfered = list_xfered + file_xfered;
+		total = list_total;
+	} else {
+		xfered = file_xfered;
+		total = file_total;
+	}
+
 	/* this is basically a switch on xfered: 0, total, and
 	 * anything else */
-	if(xfered == 0) {
-		/* set default starting values */
-		gettimeofday(&initial_time, NULL);
-		xfered_last = (off_t)0;
-		rate_last = 0.0;
-		timediff = get_update_timediff(1);
-	} else if(xfered == total) {
+	if(file_xfered == 0) {
+		/* set default starting values, ensure we only call this once
+		 * if TotalDownload is enabled */
+		if(!(config->totaldownload)
+				|| (config->totaldownload && list_xfered == 0)) {
+			gettimeofday(&initial_time, NULL);
+			xfered_last = (off_t)0;
+			rate_last = 0.0;
+			timediff = get_update_timediff(1);
+		}
+	} else if(file_xfered == file_total) {
 		/* compute final values */
 		struct timeval current_time;
 		float diff_sec, diff_usec;
@@ -446,7 +476,7 @@ void cb_dl_progress(const char *filename, off_t xfered, off_t total)
 		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 = total / (timediff * 1024.0);
+		rate = xfered / (timediff * 1024.0);
 
 		/* round elapsed time to the nearest second */
 		eta_s = (int)(timediff + 0.5);
@@ -466,7 +496,17 @@ void cb_dl_progress(const char *filename, off_t xfered, off_t total)
 		xfered_last = xfered;
 	}
 
-	percent = (int)((float)xfered) / ((float)total) * 100;
+	file_percent = (int)((float)file_xfered) / ((float)file_total) * 100;
+
+	if(config->totaldownload && list_total) {
+		total_percent = (int)((float)list_xfered + file_xfered) /
+			((float)list_total) * 100;
+
+		/* if we are at the end, add the completed file to list_xfered */
+		if(file_xfered == file_total) {
+			list_xfered += file_total;
+		}
+	}
 
 	/* fix up time for display */
 	eta_h = eta_s / 3600;
@@ -539,7 +579,7 @@ void cb_dl_progress(const char *filename, off_t xfered, off_t total)
 	free(fname);
 	free(wcfname);
 
-	fill_progress(percent, getcols() - infolen);
+	fill_progress(file_percent, total_percent, getcols() - infolen);
 	return;
 }
 
diff --git a/src/pacman/callback.h b/src/pacman/callback.h
index 28d396e..2961be8 100644
--- a/src/pacman/callback.h
+++ b/src/pacman/callback.h
@@ -34,6 +34,8 @@ void cb_trans_conv(pmtransconv_t event, void *data1, void *data2,
 void cb_trans_progress(pmtransprog_t event, const char *pkgname, int percent,
                    int howmany, int remain);
 
+/* callback to handle receipt of total download value */
+void cb_dl_total(off_t total);
 /* callback to handle display of download progress */
 void cb_dl_progress(const char *filename, off_t file_xfered, off_t file_total);
 
diff --git a/src/pacman/pacman.c b/src/pacman/pacman.c
index 66fafa1..27e4b75 100644
--- a/src/pacman/pacman.c
+++ b/src/pacman/pacman.c
@@ -844,6 +844,11 @@ int main(int argc, char *argv[])
 		cleanup(ret);
 	}
 
+	/* set TotalDownload callback if option enabled */
+	if(config->totaldownload) {
+		alpm_option_set_totaldlcb(cb_dl_total);
+	}
+
 #if defined(HAVE_GETEUID) && !defined(CYGWIN)
 	/* check if we have sufficient permission for the requested operation */
 	if(myuid > 0 && needs_transaction()) {
-- 
1.5.5.3





More information about the pacman-dev mailing list