/* callback to handle display of transaction progress */ void cb_progress(alpm_progress_t event, const char *pkgname, int percent, size_t howmany, size_t current) { static int prevpercent; static size_t prevcurrent; /* size of line to allocate for text printing (e.g. not progressbar) */ int infolen; int digits, textlen; size_t tmp; char *opr = NULL; /* used for wide character width determination and printing */ int len, wclen, wcwid, padwid; wchar_t *wcstr; const unsigned short cols = getcols(); if(config->noprogressbar || cols == 0) { return; } if(percent == 0) { get_update_timediff(1); } else if(percent == 100) { /* no need for timediff update, but unconditionally continue unless we * already completed on a previous call */ if(prevpercent == 100) { return; } } else { if(current != prevcurrent) { /* update always */ } else if(!pkgname || percent == prevpercent || get_update_timediff(0) < UPDATE_SPEED_MS) { /* only update the progress bar when we have a package name, the * percentage has changed, and it has been long enough. */ return; } } prevpercent = percent; prevcurrent = current; /* set text of message to display */ switch(event) { case ALPM_PROGRESS_ADD_START: opr = _("installing"); break; case ALPM_PROGRESS_UPGRADE_START: opr = _("upgrading"); break; case ALPM_PROGRESS_DOWNGRADE_START: opr = _("downgrading"); break; case ALPM_PROGRESS_REINSTALL_START: opr = _("reinstalling"); break; case ALPM_PROGRESS_REMOVE_START: opr = _("removing"); break; case ALPM_PROGRESS_CONFLICTS_START: opr = _("checking for file conflicts"); break; case ALPM_PROGRESS_DISKSPACE_START: opr = _("checking available disk space"); break; case ALPM_PROGRESS_INTEGRITY_START: opr = _("checking package integrity"); break; case ALPM_PROGRESS_KEYRING_START: opr = _("checking keys in keyring"); break; case ALPM_PROGRESS_LOAD_START: opr = _("loading package files"); break; default: return; } infolen = cols * 6 / 10; if(infolen < 50) { infolen = 50; } /* 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) - 1 /* space */; /* In order to deal with characters from all locales, we have to worry * about wide characters and their column widths. A lot of stuff is * done here to figure out the actual number of screen columns used * by the output, and then pad it accordingly so we fill the terminal. */ /* len = opr len + pkgname len (if available) + space + null */ len = strlen(opr) + ((pkgname) ? strlen(pkgname) : 0) + 2; wcstr = calloc(len, sizeof(wchar_t)); /* print our strings to the alloc'ed memory */ #if defined(HAVE_SWPRINTF) wclen = swprintf(wcstr, len, L"%s %s", opr, pkgname); #else /* because the format string was simple, we can easily do this without * using swprintf, although it is probably not as safe/fast. The max * chars we can copy is decremented each time by subtracting the length * of the already printed/copied wide char string. */ wclen = mbstowcs(wcstr, opr, len); wclen += mbstowcs(wcstr + wclen, " ", len - wclen); wclen += mbstowcs(wcstr + wclen, pkgname, len - wclen); #endif wcwid = wcswidth(wcstr, wclen); padwid = textlen - wcwid; /* if padwid is < 0, we need to trim the string so padwid = 0 */ if(padwid < 0) { int i = textlen - 3; wchar_t *p = wcstr; /* grab the max number of char columns we can fill */ while(i - wcwidth(*p) > 0) { i -= wcwidth(*p); p++; } /* then add the ellipsis and fill out any extra padding */ wcscpy(p, L"..."); padwid = i; } printf("(%*ld/%*ld) %ls%-*s", digits, (unsigned long)current, digits, (unsigned long)howmany, wcstr, padwid, ""); free(wcstr); /* call refactored fill progress function */ fill_progress(percent, percent, cols - infolen); if(percent == 100) { alpm_list_t *i = NULL; on_progress = 0; fflush(stdout); for(i = output; i; i = i->next) { fputs((const char *)i->data, stderr); } fflush(stderr); FREELIST(output); } else { on_progress = 1; } }
/* callback to handle display of download progress */ void cb_dl_progress(const char *filename, off_t file_xfered, off_t file_total) { 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; double rate_human, xfered_human; const char *rate_label, *xfered_label; int file_percent = 0, total_percent = 0; const unsigned short cols = getcols(); if(config->noprogressbar || cols == 0 || file_total == -1) { if(file_xfered == 0) { 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; } /* bogus values : stop here */ if(xfered > total || xfered < 0) { return; } /* this is basically a switch on xfered: 0, total, and * anything else */ if(file_xfered == 0) { /* 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(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; } 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); fname = malloc(len + 1); memcpy(fname, filename, len); /* 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; } } fname[len] = '\0'; /* 1 space + filenamelen + 1 space + 6 for size + 1 space + 3 for label + * + 2 spaces + 4 for rate + 1 for label + 2 for /s + 1 space + * 8 for eta, gives us the magic 30 */ filenamelen = infolen - 30; /* see printf() code, we omit 'HH:' in these conditions */ if(eta_h == 0 || eta_h >= 100) { filenamelen += 3; } /* In order to deal with characters from all locales, we have to worry * about wide characters and their column widths. A lot of stuff is * done here to figure out the actual number of screen columns used * by the output, and then pad it accordingly so we fill the terminal. */ /* len = filename len + null */ wcfname = calloc(len + 1, sizeof(wchar_t)); wclen = mbstowcs(wcfname, fname, len); wcwid = wcswidth(wcfname, wclen); padwid = filenamelen - wcwid; /* if padwid is < 0, we need to trim the string so padwid = 0 */ if(padwid < 0) { int i = filenamelen - 3; wchar_t *wcp = wcfname; /* grab the max number of char columns we can fill */ while(i > 0 && wcwidth(*wcp) < i) { i -= wcwidth(*wcp); wcp++; } /* then add the ellipsis and fill out any extra padding */ wcscpy(wcp, L"..."); padwid = i; } rate_human = humanize_size((off_t)rate, '\0', -1, &rate_label); xfered_human = humanize_size(xfered, '\0', -1, &xfered_label); printf(" %ls%-*s ", wcfname, padwid, ""); /* We will show 1.62M/s, 11.6M/s, but 116K/s and 1116K/s */ if(rate_human < 9.995) { printf("%6.1f %3s %4.2f%c/s ", xfered_human, xfered_label, rate_human, rate_label[0]); } else if(rate_human < 99.95) { printf("%6.1f %3s %4.1f%c/s ", xfered_human, xfered_label, rate_human, rate_label[0]); } else { printf("%6.1f %3s %4.f%c/s ", xfered_human, xfered_label, rate_human, rate_label[0]); } if(eta_h == 0) { printf("%02u:%02u", eta_m, eta_s); } else if(eta_h < 100) { printf("%02u:%02u:%02u", eta_h, eta_m, eta_s); } else { fputs("--:--", stdout); } free(fname); free(wcfname); if(totaldownload) { fill_progress(file_percent, total_percent, cols - infolen); } else { fill_progress(file_percent, file_percent, cols - infolen); } return; }
/* callback to handle display of download progress */ void cb_dl_progress(const char *filename, off_t file_xfered, off_t file_total) { 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, timediff = 0.0; unsigned int eta_h = 0, eta_m = 0, eta_s = 0; double rate_human, xfered_human; const char *rate_label, *xfered_label; int file_percent = 0, total_percent = 0; const int cols = getcols(); if(config->noprogressbar || cols == 0 || file_total == -1) { if(file_xfered == 0) { printf(_("downloading %s...\n"), filename); fflush(stdout); } return; } infolen = cols * 6 / 10; if(infolen < 50) { infolen = 50; } /* explanation of magic 28 number at the end */ filenamelen = infolen - 28; /* 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; } /* bogus values : stop here */ if(xfered > total) { return; } /* this is basically a switch on xfered: 0, total, and * anything else */ if(file_xfered == 0) { /* set default starting values, ensure we only call this once * if TotalDownload is enabled */ if(!totaldownload || (totaldownload && list_xfered == 0)) { gettimeofday(&initial_time, NULL); xfered_last = (off_t)0; rate_last = 0.0; get_update_timediff(1); } } else if(file_xfered == file_total) { /* compute final values */ struct timeval current_time; double diff_sec, diff_usec; gettimeofday(¤t_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 = xfered / timediff; /* round elapsed time to the nearest second */ eta_s = (int)(timediff + 0.5); } 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 = (xfered - xfered_last) / timediff; /* average rate to reduce jumpiness */ rate = (rate + 2 * rate_last) / 3; eta_s = (total - xfered) / rate; rate_last = rate; xfered_last = xfered; } file_percent = (file_xfered * 100) / file_total; 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; fname = strdup(filename); /* strip package or DB extension for cleaner look */ if((p = strstr(fname, ".pkg")) || (p = strstr(fname, ".db"))) { *p = '\0'; } /* In order to deal with characters from all locales, we have to worry * about wide characters and their column widths. A lot of stuff is * done here to figure out the actual number of screen columns used * by the output, and then pad it accordingly so we fill the terminal. */ /* len = filename len + null */ len = strlen(filename) + 1; wcfname = calloc(len, sizeof(wchar_t)); wclen = mbstowcs(wcfname, fname, len); wcwid = wcswidth(wcfname, wclen); padwid = filenamelen - wcwid; /* if padwid is < 0, we need to trim the string so padwid = 0 */ if(padwid < 0) { int i = filenamelen - 3; wchar_t *wcp = wcfname; /* grab the max number of char columns we can fill */ while(i > 0 && wcwidth(*wcp) < i) { i -= wcwidth(*wcp); wcp++; } /* then add the ellipsis and fill out any extra padding */ wcscpy(wcp, L"..."); padwid = i; } rate_human = humanize_size((off_t)rate, '\0', 0, &rate_label); xfered_human = humanize_size(xfered, '\0', 0, &xfered_label); /* 1 space + filenamelen + 1 space + 7 for size + 1 + 7 for rate + 2 for /s + 1 space + 8 for eta */ printf(" %ls%-*s %6.1f%s %#6.1f%s/s %02u:%02u:%02u", wcfname, padwid, "", xfered_human, xfered_label, rate_human, rate_label, eta_h, eta_m, eta_s); free(fname); free(wcfname); if(totaldownload) { fill_progress(file_percent, total_percent, cols - infolen); } else { fill_progress(file_percent, file_percent, cols - infolen); } return; }