summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/libalpm/be_sync.c1
-rw-r--r--lib/libalpm/dload.c61
-rw-r--r--lib/libalpm/dload.h2
-rw-r--r--src/pacman/callback.c387
-rw-r--r--src/pacman/callback.h4
-rw-r--r--src/pacman/sync.c2
-rw-r--r--src/pacman/util.c18
-rw-r--r--src/pacman/util.h4
8 files changed, 320 insertions, 159 deletions
diff --git a/lib/libalpm/be_sync.c b/lib/libalpm/be_sync.c
index add1a576..86015ab6 100644
--- a/lib/libalpm/be_sync.c
+++ b/lib/libalpm/be_sync.c
@@ -366,6 +366,7 @@ int SYMEXPORT alpm_dbs_update(alpm_handle_t *handle, alpm_list_t *dbs, int force
if(siglevel & ALPM_SIG_DATABASE) {
struct dload_payload *sig_payload;
CALLOC(sig_payload, 1, sizeof(*sig_payload), GOTO_ERR(handle, ALPM_ERR_MEMORY, cleanup));
+ sig_payload->signature = 1;
/* print filename into a buffer (leave space for separator and .sig) */
len = strlen(db->treename) + strlen(dbext) + 5;
diff --git a/lib/libalpm/dload.c b/lib/libalpm/dload.c
index 2f360ab8..9ab0e5c4 100644
--- a/lib/libalpm/dload.c
+++ b/lib/libalpm/dload.c
@@ -90,6 +90,11 @@ static int dload_progress_cb(void *file, curl_off_t dltotal, curl_off_t dlnow,
off_t current_size, total_size;
alpm_download_event_progress_t cb_data = {0};
+ /* do not print signature files progress bar */
+ if(payload->signature) {
+ return 0;
+ }
+
/* avoid displaying progress bar for redirects with a body */
if(payload->respcode >= 300) {
return 0;
@@ -100,6 +105,11 @@ static int dload_progress_cb(void *file, curl_off_t dltotal, curl_off_t dlnow,
return 1;
}
+ if(dlnow < 0 || dltotal <= 0 || dlnow > dltotal) {
+ /* bogus values : stop here */
+ return 0;
+ }
+
current_size = payload->initial_size + dlnow;
/* is our filesize still under any set limit? */
@@ -115,34 +125,15 @@ static int dload_progress_cb(void *file, curl_off_t dltotal, curl_off_t dlnow,
total_size = payload->initial_size + dltotal;
- if(dltotal == 0 || payload->prevprogress == total_size) {
+ if(payload->prevprogress == total_size) {
return 0;
}
- /* initialize the progress bar here to avoid displaying it when
- * a repo is up to date and nothing gets downloaded.
- * payload->handle->dlcb will receive the remote_name
- * and the following arguments:
- * 0, -1: download initialized
- * 0, 0: non-download event
- * x {x>0}, x: download complete
- * x {x>0, x<y}, y {y > 0}: download progress, expected total is known */
- if(!payload->cb_initialized) {
- cb_data.downloaded = 0;
- cb_data.total = -1;
- payload->cb_initialized = 1;
- }
- if(payload->prevprogress == current_size) {
- cb_data.downloaded = 0;
- cb_data.total = 0;
- } else {
/* do NOT include initial_size since it wasn't part of the package's
* download_size (nor included in the total download size callback) */
- cb_data.downloaded = dlnow;
- cb_data.total = dltotal;
- }
+ cb_data.total = dltotal;
+ cb_data.downloaded = dlnow;
payload->handle->dlcb(payload->remote_name, ALPM_DOWNLOAD_PROGRESS, &cb_data);
-
payload->prevprogress = current_size;
return 0;
@@ -375,6 +366,8 @@ static int curl_download_internal(struct dload_payload *payload,
double remote_size, bytes_dl;
struct sigaction orig_sig_pipe, orig_sig_int;
CURLcode curlerr;
+ alpm_download_event_init_t init_cb_data = {0};
+ alpm_download_event_completed_t completed_cb_data = {0};
/* shortcut to our handle within the payload */
alpm_handle_t *handle = payload->handle;
CURL *curl = curl_easy_init();
@@ -444,6 +437,8 @@ static int curl_download_internal(struct dload_payload *payload,
dload_interrupted = 0;
mask_signal(SIGINT, &inthandler, &orig_sig_int);
+ handle->dlcb(payload->remote_name, ALPM_DOWNLOAD_INIT, &init_cb_data);
+
/* perform transfer */
curlerr = curl_easy_perform(curl);
_alpm_log(handle, ALPM_LOG_DEBUG, "curl returned error %d from transfer\n",
@@ -604,6 +599,10 @@ cleanup:
raise(SIGINT);
}
+ completed_cb_data.total = bytes_dl;
+ completed_cb_data.result = ret;
+ handle->dlcb(payload->remote_name, ALPM_DOWNLOAD_COMPLETED, &completed_cb_data);
+
return ret;
}
@@ -665,7 +664,6 @@ static int curl_multi_check_finished_download(CURLM *curlm, CURLMsg *msg,
long remote_time = -1;
struct stat st;
char hostname[HOSTNAME_SIZE];
- alpm_download_event_completed_t cb_data = {0};
int ret = -1;
curlerr = curl_easy_getinfo(curl, CURLINFO_PRIVATE, &payload);
@@ -825,9 +823,12 @@ cleanup:
unlink(payload->tempfile_name);
}
- cb_data.total = bytes_dl;
- cb_data.result = ret;
- handle->dlcb(payload->remote_name, ALPM_DOWNLOAD_COMPLETED, &cb_data);
+ if(!payload->signature) {
+ alpm_download_event_completed_t cb_data = {0};
+ cb_data.total = bytes_dl;
+ cb_data.result = ret;
+ handle->dlcb(payload->remote_name, ALPM_DOWNLOAD_COMPLETED, &cb_data);
+ }
curl_multi_remove_handle(curlm, curl);
curl_easy_cleanup(curl);
@@ -945,9 +946,11 @@ static int curl_multi_download_internal(alpm_handle_t *handle,
struct dload_payload *payload = payloads->data;
if(curl_multi_add_payload(handle, curlm, payload, localpath) == 0) {
- alpm_download_event_init_t cb_data = {.optional = payload->errors_ok};
+ if(!payload->signature) {
+ alpm_download_event_init_t cb_data = {.optional = payload->errors_ok};
+ handle->dlcb(payload->remote_name, ALPM_DOWNLOAD_INIT, &cb_data);
+ }
- handle->dlcb(payload->remote_name, ALPM_DOWNLOAD_INIT, &cb_data);
payloads = payloads->next;
// TODO: report that download has started
} else {
@@ -1129,6 +1132,7 @@ char SYMEXPORT *alpm_fetch_pkgurl(alpm_handle_t *handle, const char *url)
sig_filepath = filecache_find_url(handle, payload.fileurl);
if(sig_filepath == NULL) {
+ payload.signature = 1;
payload.handle = handle;
payload.trust_remote_name = 1;
payload.force = 1;
@@ -1184,5 +1188,4 @@ void _alpm_dload_payload_reset_for_retry(struct dload_payload *payload)
payload->initial_size += payload->prevprogress;
payload->prevprogress = 0;
payload->unlink_on_fail = 0;
- payload->cb_initialized = 0;
}
diff --git a/lib/libalpm/dload.h b/lib/libalpm/dload.h
index a40b51b7..1e4af755 100644
--- a/lib/libalpm/dload.h
+++ b/lib/libalpm/dload.h
@@ -42,7 +42,7 @@ struct dload_payload {
int errors_ok;
int unlink_on_fail;
int trust_remote_name;
- int cb_initialized;
+ int signature; /* specifies if the payload is a signature file */
#ifdef HAVE_LIBCURL
CURL *curl;
char error_buffer[CURL_ERROR_SIZE];
diff --git a/src/pacman/callback.c b/src/pacman/callback.c
index 613d59d4..c2e516ec 100644
--- a/src/pacman/callback.c
+++ b/src/pacman/callback.c
@@ -18,6 +18,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <assert.h>
+#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -52,6 +54,50 @@ static alpm_list_t *output = NULL;
#define CLOCK_MONOTONIC_COARSE CLOCK_MONOTONIC
#endif
+struct pacman_progress_bar {
+ const char *filename;
+ off_t xfered;
+ off_t total_size;
+ uint64_t init_time; /* Time when this download started doing any progress */
+ uint64_t sync_time; /* Last time we updated the bar info */
+ double rate;
+ unsigned int eta; /* ETA in seconds */
+ bool completed; /* transfer is completed */
+};
+
+/* This datastruct represents the state of multiline progressbar UI */
+struct pacman_multibar_ui {
+ /* List of active downloads handled by multibar UI.
+ * Once the first download in the list is completed it is removed
+ * from this list and we never redraw it anymore.
+ * If the download is in this list, then the UI can redraw the progress bar or change
+ * the order of the bars (e.g. moving completed bars to the top of the list)
+ */
+ alpm_list_t *active_downloads; /* List of type 'struct pacman_progress_bar' */
+
+ /* Number of active download bars that multibar UI handles. */
+ size_t active_downloads_num;
+
+ /* Specifies whether a completed progress bar need to be reordered and moved
+ * to the top of the list.
+ */
+ bool move_completed_up;
+
+ /* Cursor position relative to the first active progress bar,
+ * e.g. 0 means the first active progress bar, active_downloads_num-1 means the last bar,
+ * active_downloads_num - is the line below all progress bars.
+ */
+ int cursor_lineno;
+};
+
+struct pacman_multibar_ui multibar_ui = {0};
+
+static void cursor_goto_end(void);
+
+void multibar_move_completed_up(bool value) {
+ multibar_ui.move_completed_up = value;
+}
+
static int64_t get_time_ms(void)
{
#if defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0) && defined(CLOCK_MONOTONIC_COARSE)
@@ -152,11 +198,7 @@ static void fill_progress(const int bar_percent, const int disp_percent,
printf(" %3d%%", disp_percent);
}
- if(bar_percent == 100) {
- putchar('\n');
- } else {
- putchar('\r');
- }
+ putchar('\r');
fflush(stdout);
}
@@ -346,6 +388,7 @@ void cb_event(alpm_event_t *event)
case ALPM_EVENT_DB_RETRIEVE_FAILED:
case ALPM_EVENT_PKG_RETRIEVE_DONE:
case ALPM_EVENT_PKG_RETRIEVE_FAILED:
+ cursor_goto_end();
flush_output_list();
on_progress = 0;
break;
@@ -629,6 +672,7 @@ void cb_progress(alpm_progress_t event, const char *pkgname, int percent,
fill_progress(percent, percent, cols - infolen);
if(percent == 100) {
+ putchar('\n');
flush_output_list();
on_progress = 0;
} else {
@@ -647,149 +691,87 @@ void cb_dl_total(off_t total)
}
}
-/* callback to handle display of download progress */
-static void dload_progress_event(const char *filename, off_t file_xfered, off_t file_total)
+static int dload_progressbar_enabled(void)
+{
+ return !config->noprogressbar && (getcols() != 0);
+}
+
+/* Goto the line that corresponds to num-th active download */
+static void cursor_goto_bar(int num)
+{
+ if(num > multibar_ui.cursor_lineno) {
+ console_cursor_move_down(num - multibar_ui.cursor_lineno);
+ } else if(num < multibar_ui.cursor_lineno) {
+ console_cursor_move_up(multibar_ui.cursor_lineno - num);
+ }
+ multibar_ui.cursor_lineno = num;
+}
+
+/* Goto the line *after* the last active progress bar */
+static void cursor_goto_end(void)
+{
+ cursor_goto_bar(multibar_ui.active_downloads_num);
+}
+
+/* Returns true if element with the specified name is found, false otherwise */
+static bool find_bar_for_filename(const char *filename, int *index, struct pacman_progress_bar **bar)
+{
+ int i = 0;
+ alpm_list_t *listitem = multibar_ui.active_downloads;
+ for(; listitem; listitem = listitem->next, i++) {
+ struct pacman_progress_bar *b = listitem->data;
+ if (strcmp(b->filename, filename) == 0) {
+ /* we found a progress bar with the given name */
+ *index = i;
+ *bar = b;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static void draw_pacman_progress_bar(struct pacman_progress_bar *bar)
{
- static double rate_last;
- static off_t xfered_last;
- static int64_t initial_time = 0;
int infolen;
int filenamelen;
char *fname, *p;
/* used for wide character width determination and printing */
int len, wclen, wcwid, padwid;
wchar_t *wcfname;
-
- int totaldownload = 0;
- off_t xfered, total;
- double rate = 0.0;
- unsigned int eta_h = 0, eta_m = 0, eta_s = 0;
+ unsigned int eta_h = 0, eta_m = 0, eta_s = bar->eta;
double rate_human, xfered_human;
const char *rate_label, *xfered_label;
- int file_percent = 0, total_percent = 0;
+ int file_percent = 0;
const unsigned short cols = getcols();
- /* Nothing has changed since last callback; stop here */
- if(file_xfered == 0 && file_total == 0) {
- return;
- }
-
- if(config->noprogressbar || cols == 0) {
- if(file_xfered == 0 && file_total == -1) {
- printf(_("downloading %s...\n"), filename);
- fflush(stdout);
- }
- return;
- }
-
- infolen = cols * 6 / 10;
- if(infolen < 50) {
- infolen = 50;
- }
- /* only use TotalDownload if enabled and we have a callback value */
- if(config->totaldownload && list_total) {
- /* sanity check */
- if(list_xfered + file_total <= list_total) {
- totaldownload = 1;
- } else {
- /* bogus values : don't enable totaldownload and reset */
- list_xfered = 0;
- list_total = 0;
- }
- }
-
- if(totaldownload) {
- 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(file_xfered == 0 && file_total == -1) {
- /* set default starting values, ensure we only call this once
- * if TotalDownload is enabled */
- if(!totaldownload || (totaldownload && list_xfered == 0)) {
- initial_time = get_time_ms();
- xfered_last = (off_t)0;
- rate_last = 0.0;
- get_update_timediff(1);
- }
- } else if(xfered > total || xfered < 0) {
- /* bogus values : stop here */
- return;
- } else if(file_xfered == file_total) {
- /* compute final values */
- int64_t timediff = get_time_ms() - initial_time;
- if(timediff > 0) {
- rate = (double)xfered / (timediff / 1000.0);
- /* round elapsed time (in ms) to the nearest second */
- eta_s = (unsigned int)(timediff + 500) / 1000;
- } else {
- eta_s = 0;
- }
- } else {
- /* compute current average values */
- int64_t timediff = get_update_timediff(0);
-
- if(timediff < UPDATE_SPEED_MS) {
- /* return if the calling interval was too short */
- return;
- }
- rate = (double)(xfered - xfered_last) / (timediff / 1000.0);
- /* average rate to reduce jumpiness */
- rate = (rate + 2 * rate_last) / 3;
- if(rate > 0.0) {
- eta_s = (total - xfered) / rate;
- } else {
- eta_s = UINT_MAX;
- }
- rate_last = rate;
- xfered_last = xfered;
- }
-
- if(file_total) {
- file_percent = (file_xfered * 100) / file_total;
+ if(bar->total_size) {
+ file_percent = (bar->xfered * 100) / bar->total_size;
} else {
file_percent = 100;
}
- if(totaldownload) {
- total_percent = ((list_xfered + file_xfered) * 100) /
- list_total;
-
- /* 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;
eta_s -= eta_h * 3600;
eta_m = eta_s / 60;
eta_s -= eta_m * 60;
- len = strlen(filename);
+ len = strlen(bar->filename);
fname = malloc(len + 1);
- memcpy(fname, filename, len + 1);
+ memcpy(fname, bar->filename, len + 1);
/* strip package or DB extension for cleaner look */
if((p = strstr(fname, ".pkg")) || (p = strstr(fname, ".db")) || (p = strstr(fname, ".files"))) {
- /* tack on a .sig suffix for signatures */
- if(memcmp(&filename[len - 4], ".sig", 4) == 0) {
- memcpy(p, ".sig", 4);
-
- /* adjust length for later calculations */
- len = p - fname + 4;
- } else {
- len = p - fname;
- }
+ len = p - fname;
fname[len] = '\0';
}
+ infolen = cols * 6 / 10;
+ if(infolen < 50) {
+ infolen = 50;
+ }
+
/* 1 space + filenamelen + 1 space + 6 for size + 1 space + 3 for label +
* + 2 spaces + 4 for rate + 1 space + 3 for label + 2 for /s + 1 space +
* 8 for eta, gives us the magic 33 */
@@ -824,8 +806,8 @@ static void dload_progress_event(const char *filename, off_t file_xfered, off_t
}
- rate_human = humanize_size((off_t)rate, '\0', -1, &rate_label);
- xfered_human = humanize_size(xfered, '\0', -1, &xfered_label);
+ rate_human = humanize_size((off_t)bar->rate, '\0', -1, &rate_label);
+ xfered_human = humanize_size(bar->xfered, '\0', -1, &xfered_label);
printf(" %ls%-*s ", wcfname, padwid, "");
/* We will show 1.62 MiB/s, 11.6 MiB/s, but 116 KiB/s and 1116 KiB/s */
@@ -850,19 +832,166 @@ static void dload_progress_event(const char *filename, off_t file_xfered, off_t
free(fname);
free(wcfname);
- if(totaldownload) {
- fill_progress(file_percent, total_percent, cols - infolen);
+ fill_progress(file_percent, file_percent, cols - infolen);
+ return;
+}
+
+static void dload_init_event(const char *filename, alpm_download_event_init_t *data)
+{
+ (void)data;
+
+ if(!dload_progressbar_enabled()) {
+ printf(_(" %s downloading...\n"), filename);
+ return;
+ }
+
+ struct pacman_progress_bar *bar = calloc(1, sizeof(struct pacman_progress_bar));
+ assert(bar);
+ bar->filename = filename;
+ bar->init_time = get_time_ms();
+ bar->rate = 0.0;
+ multibar_ui.active_downloads = alpm_list_add(multibar_ui.active_downloads, bar);
+
+ cursor_goto_end();
+ printf(_(" %s downloading...\n"), filename);
+ multibar_ui.cursor_lineno++;
+ multibar_ui.active_downloads_num++;
+}
+
+/* Draws download progress */
+static void dload_progress_event(const char *filename, alpm_download_event_progress_t *data)
+{
+ int index;
+ struct pacman_progress_bar *bar;
+ int64_t curr_time = get_time_ms();
+ double last_chunk_rate;
+ int64_t timediff;
+
+ if(!dload_progressbar_enabled()) {
+ return;
+ }
+
+ assert(find_bar_for_filename(filename, &index, &bar));
+
+ /* compute current average values */
+ timediff = curr_time - bar->sync_time;
+
+ if(timediff < UPDATE_SPEED_MS) {
+ /* return if the calling interval was too short */
+ return;
+ }
+ bar->sync_time = curr_time;
+
+ last_chunk_rate = (double)(data->downloaded - bar->xfered) / (timediff / 1000.0);
+ /* average rate to reduce jumpiness */
+ bar->rate = (last_chunk_rate + 2 * bar->rate) / 3;
+ if(bar->rate > 0.0) {
+ bar->eta = (data->total - data->downloaded) / bar->rate;
} else {
- fill_progress(file_percent, file_percent, cols - infolen);
+ bar->eta = UINT_MAX;
+ }
+
+ /* Total size is received after the download starts. */
+ bar->total_size = data->total;
+ bar->xfered = data->downloaded;
+
+ cursor_goto_bar(index);
+ draw_pacman_progress_bar(bar);
+ fflush(stdout);
+}
+
+/* download completed */
+static void dload_complete_event(const char *filename, alpm_download_event_completed_t *data)
+{
+ int index;
+ struct pacman_progress_bar *bar;
+ int64_t timediff;
+
+ if(!dload_progressbar_enabled()) {
+ return;
+ }
+
+ assert(find_bar_for_filename(filename, &index, &bar));
+ bar->completed = true;
+
+ /* This may not have been initialized if the download finished before
+ * an alpm_download_event_progress_t event happened */
+ bar->total_size = data->total;
+
+ if(data->result == 1) {
+ cursor_goto_bar(index);
+ printf(_(" %s is up to date"), bar->filename);
+ /* The line contains text from previous status. Erase these leftovers. */
+ console_erase_line();
+ } else if(data->result == 0) {
+ /* compute final values */
+ bar->xfered = bar->total_size;
+ timediff = get_time_ms() - bar->init_time;
+
+ /* if transfer was too fast, treat it as a 1ms transfer, for the sake
+ * of the rate calculation */
+ if(timediff < 1)
+ timediff = 1;
+
+ bar->rate = (double)bar->xfered / (timediff / 1000.0);
+ /* round elapsed time (in ms) to the nearest second */
+ bar->eta = (unsigned int)(timediff + 500) / 1000;
+
+ if(multibar_ui.move_completed_up && index != 0) {
+ /* If this item completed then move it to the top.
+ * Swap 0-th bar data with `index`-th one
+ */
+ struct pacman_progress_bar *former_topbar = multibar_ui.active_downloads->data;
+ alpm_list_t *baritem = alpm_list_nth(multibar_ui.active_downloads, index);
+ multibar_ui.active_downloads->data = bar;
+ baritem->data = former_topbar;
+
+ cursor_goto_bar(index);
+ draw_pacman_progress_bar(former_topbar);
+
+ index = 0;
+ }
+
+ cursor_goto_bar(index);
+ draw_pacman_progress_bar(bar);
+ } else {
+ cursor_goto_bar(index);
+ printf(_(" %s failed to download"), bar->filename);
+ console_erase_line();
+ }
+ fflush(stdout);
+
+ /* If the first bar is completed then there is no reason to keep it
+ * in the list as we are not going to redraw it anymore.
+ */
+ while(multibar_ui.active_downloads) {
+ alpm_list_t *head = multibar_ui.active_downloads;
+ struct pacman_progress_bar *j = head->data;
+ if(j->completed) {
+ multibar_ui.cursor_lineno--;
+ multibar_ui.active_downloads_num--;
+ multibar_ui.active_downloads = alpm_list_remove_item(
+ multibar_ui.active_downloads, head);
+ free(head);
+ free(j);
+ } else {
+ break;
+ }
}
- return;
}
+/* Callback to handle display of download progress */
void cb_download(const char *filename, alpm_download_event_type_t event, void *data)
{
- if(event == ALPM_DOWNLOAD_PROGRESS) {
- alpm_download_event_progress_t *progress = data;
- dload_progress_event(filename, progress->downloaded, progress->total);
+ if(event == ALPM_DOWNLOAD_INIT) {
+ dload_init_event(filename, data);
+ } else if(event == ALPM_DOWNLOAD_PROGRESS) {
+ dload_progress_event(filename, data);
+ } else if(event == ALPM_DOWNLOAD_COMPLETED) {
+ dload_complete_event(filename, data);
+ } else {
+ pm_printf(ALPM_LOG_ERROR, _("unknown callback event type %d for %s\n"),
+ event, filename);
}
}
diff --git a/src/pacman/callback.h b/src/pacman/callback.h
index 6d92e86b..09d544a6 100644
--- a/src/pacman/callback.h
+++ b/src/pacman/callback.h
@@ -20,6 +20,7 @@
#ifndef PM_CALLBACK_H
#define PM_CALLBACK_H
+#include <stdbool.h>
#include <sys/types.h> /* off_t */
#include <alpm.h>
@@ -44,4 +45,7 @@ void cb_download(const char *filename, alpm_download_event_type_t event,
__attribute__((format(printf, 2, 0)))
void cb_log(alpm_loglevel_t level, const char *fmt, va_list args);
+/* specify if multibar UI should move completed bars to the top of the screen */
+void multibar_move_completed_up(bool value);
+
#endif /* PM_CALLBACK_H */
diff --git a/src/pacman/sync.c b/src/pacman/sync.c
index f7dcb958..a05af5da 100644
--- a/src/pacman/sync.c
+++ b/src/pacman/sync.c
@@ -35,6 +35,7 @@
#include "pacman.h"
#include "util.h"
#include "package.h"
+#include "callback.h"
#include "conf.h"
static int unlink_verbose(const char *pathname, int ignore_missing)
@@ -824,6 +825,7 @@ int sync_prepare_execute(void)
goto cleanup;
}
+ multibar_move_completed_up(true);
if(alpm_trans_commit(config->handle, &data) == -1) {
alpm_errno_t err = alpm_errno(config->handle);
pm_printf(ALPM_LOG_ERROR, _("failed to commit transaction (%s)\n"),
diff --git a/src/pacman/util.c b/src/pacman/util.c
index 97b8e06d..03035037 100644
--- a/src/pacman/util.c
+++ b/src/pacman/util.c
@@ -1837,3 +1837,21 @@ char *arg_to_string(int argc, char *argv[])
strcpy(p, argv[i]);
return cl_text;
}
+
+/* Moves console cursor `lines` up */
+void console_cursor_move_up(unsigned int lines)
+{
+ printf("\x1B[%dF", lines);
+}
+
+/* Moves console cursor `lines` down */
+void console_cursor_move_down(unsigned int lines)
+{
+ printf("\x1B[%dE", lines);
+}
+
+/* Erases line from the current cursor position till the end of the line */
+void console_erase_line(void)
+{
+ printf("\x1B[K");
+}
diff --git a/src/pacman/util.h b/src/pacman/util.h
index 2b21f3d5..c97048fb 100644
--- a/src/pacman/util.h
+++ b/src/pacman/util.h
@@ -83,6 +83,10 @@ char *arg_to_string(int argc, char *argv[]);
char *safe_fgets_stdin(char *s, int size);
void console_cursor_hide(void);
void console_cursor_show(void);
+void console_cursor_move_up(unsigned int lines);
+void console_cursor_move_down(unsigned int lines);
+/* Erases line from the current cursor position till the end of the line */
+void console_erase_line(void);
int pm_printf(alpm_loglevel_t level, const char *format, ...) __attribute__((format(printf,2,3)));
int pm_asprintf(char **string, const char *format, ...) __attribute__((format(printf,2,3)));