libalpm
Arch Linux Package Manager Library
|
00001 /* 00002 * callback.c 00003 * 00004 * Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org> 00005 * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org> 00006 * 00007 * This program is free software; you can redistribute it and/or modify 00008 * it under the terms of the GNU General Public License as published by 00009 * the Free Software Foundation; either version 2 of the License, or 00010 * (at your option) any later version. 00011 * 00012 * This program is distributed in the hope that it will be useful, 00013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00015 * GNU General Public License for more details. 00016 * 00017 * You should have received a copy of the GNU General Public License 00018 * along with this program. If not, see <http://www.gnu.org/licenses/>. 00019 */ 00020 00021 #include <stdio.h> 00022 #include <stdlib.h> 00023 #include <string.h> 00024 #include <sys/time.h> 00025 #include <sys/types.h> /* off_t */ 00026 #include <time.h> 00027 #include <unistd.h> 00028 #include <wchar.h> 00029 #include <limits.h> /* UINT_MAX */ 00030 00031 #include <alpm.h> 00032 00033 /* pacman */ 00034 #include "callback.h" 00035 #include "util.h" 00036 #include "conf.h" 00037 00038 /* download progress bar */ 00039 static off_t list_xfered = 0.0; 00040 static off_t list_total = 0.0; 00041 00042 /* delayed output during progress bar */ 00043 static int on_progress = 0; 00044 static alpm_list_t *output = NULL; 00045 00046 /* update speed for the fill_progress based functions */ 00047 #define UPDATE_SPEED_MS 200 00048 00049 /** 00050 * Silly little helper function, determines if the caller needs a visual update 00051 * since the last time this function was called. 00052 * This is made for the two progress bar functions, to prevent flicker. 00053 * @param first_call 1 on first call for initialization purposes, 0 otherwise 00054 * @return number of milliseconds since last call 00055 */ 00056 static long get_update_timediff(int first_call) 00057 { 00058 long retval = 0; 00059 static struct timeval last_time = {0, 0}; 00060 00061 /* on first call, simply set the last time and return */ 00062 if(first_call) { 00063 gettimeofday(&last_time, NULL); 00064 } else { 00065 struct timeval this_time; 00066 time_t diff_sec; 00067 suseconds_t diff_usec; 00068 00069 gettimeofday(&this_time, NULL); 00070 diff_sec = this_time.tv_sec - last_time.tv_sec; 00071 diff_usec = this_time.tv_usec - last_time.tv_usec; 00072 00073 retval = (diff_sec * 1000) + (diff_usec / 1000); 00074 00075 /* do not update last_time if interval was too short */ 00076 if(retval >= UPDATE_SPEED_MS) { 00077 last_time = this_time; 00078 } 00079 } 00080 00081 return retval; 00082 } 00083 00084 /* refactored from cb_trans_progress */ 00085 static void fill_progress(const int bar_percent, const int disp_percent, 00086 const int proglen) 00087 { 00088 /* 8 = 1 space + 1 [ + 1 ] + 5 for percent */ 00089 const int hashlen = proglen - 8; 00090 const int hash = bar_percent * hashlen / 100; 00091 static int lasthash = 0, mouth = 0; 00092 int i; 00093 00094 if(bar_percent == 0) { 00095 lasthash = 0; 00096 mouth = 0; 00097 } 00098 00099 if(hashlen > 0) { 00100 fputs(" [", stdout); 00101 for(i = hashlen; i > 0; --i) { 00102 /* if special progress bar enabled */ 00103 if(config->chomp) { 00104 if(i > hashlen - hash) { 00105 putchar('-'); 00106 } else if(i == hashlen - hash) { 00107 if(lasthash == hash) { 00108 if(mouth) { 00109 fputs("\033[1;33mC\033[m", stdout); 00110 } else { 00111 fputs("\033[1;33mc\033[m", stdout); 00112 } 00113 } else { 00114 lasthash = hash; 00115 mouth = mouth == 1 ? 0 : 1; 00116 if(mouth) { 00117 fputs("\033[1;33mC\033[m", stdout); 00118 } else { 00119 fputs("\033[1;33mc\033[m", stdout); 00120 } 00121 } 00122 } else if(i % 3 == 0) { 00123 fputs("\033[0;37mo\033[m", stdout); 00124 } else { 00125 fputs("\033[0;37m \033[m", stdout); 00126 } 00127 } /* else regular progress bar */ 00128 else if(i > hashlen - hash) { 00129 putchar('#'); 00130 } else { 00131 putchar('-'); 00132 } 00133 } 00134 putchar(']'); 00135 } 00136 /* print display percent after progress bar */ 00137 /* 5 = 1 space + 3 digits + 1 % */ 00138 if(proglen >= 5) { 00139 printf(" %3d%%", disp_percent); 00140 } 00141 00142 if(bar_percent == 100) { 00143 putchar('\n'); 00144 } else { 00145 putchar('\r'); 00146 } 00147 fflush(stdout); 00148 } 00149 00150 00151 00152 /* callback to handle messages/notifications from libalpm transactions */ 00153 void cb_event(alpm_event_t event, void *data1, void *data2) 00154 { 00155 if(config->print) { 00156 return; 00157 } 00158 switch(event) { 00159 case ALPM_EVENT_CHECKDEPS_START: 00160 printf(_("checking dependencies...\n")); 00161 break; 00162 case ALPM_EVENT_FILECONFLICTS_START: 00163 if(config->noprogressbar) { 00164 printf(_("checking for file conflicts...\n")); 00165 } 00166 break; 00167 case ALPM_EVENT_RESOLVEDEPS_START: 00168 printf(_("resolving dependencies...\n")); 00169 break; 00170 case ALPM_EVENT_INTERCONFLICTS_START: 00171 printf(_("looking for inter-conflicts...\n")); 00172 break; 00173 case ALPM_EVENT_ADD_START: 00174 if(config->noprogressbar) { 00175 printf(_("installing %s...\n"), alpm_pkg_get_name(data1)); 00176 } 00177 break; 00178 case ALPM_EVENT_ADD_DONE: 00179 alpm_logaction(config->handle, "installed %s (%s)\n", 00180 alpm_pkg_get_name(data1), 00181 alpm_pkg_get_version(data1)); 00182 display_optdepends(data1); 00183 break; 00184 case ALPM_EVENT_REMOVE_START: 00185 if(config->noprogressbar) { 00186 printf(_("removing %s...\n"), alpm_pkg_get_name(data1)); 00187 } 00188 break; 00189 case ALPM_EVENT_REMOVE_DONE: 00190 alpm_logaction(config->handle, "removed %s (%s)\n", 00191 alpm_pkg_get_name(data1), 00192 alpm_pkg_get_version(data1)); 00193 break; 00194 case ALPM_EVENT_UPGRADE_START: 00195 if(config->noprogressbar) { 00196 printf(_("upgrading %s...\n"), alpm_pkg_get_name(data1)); 00197 } 00198 break; 00199 case ALPM_EVENT_UPGRADE_DONE: 00200 alpm_logaction(config->handle, "upgraded %s (%s -> %s)\n", 00201 alpm_pkg_get_name(data1), 00202 alpm_pkg_get_version(data2), 00203 alpm_pkg_get_version(data1)); 00204 display_new_optdepends(data2,data1); 00205 break; 00206 case ALPM_EVENT_INTEGRITY_START: 00207 if(config->noprogressbar) { 00208 printf(_("checking package integrity...\n")); 00209 } 00210 break; 00211 case ALPM_EVENT_LOAD_START: 00212 if(config->noprogressbar) { 00213 printf(_("loading package files...\n")); 00214 } 00215 break; 00216 case ALPM_EVENT_DELTA_INTEGRITY_START: 00217 printf(_("checking delta integrity...\n")); 00218 break; 00219 case ALPM_EVENT_DELTA_PATCHES_START: 00220 printf(_("applying deltas...\n")); 00221 break; 00222 case ALPM_EVENT_DELTA_PATCH_START: 00223 printf(_("generating %s with %s... "), (char *)data1, (char *)data2); 00224 break; 00225 case ALPM_EVENT_DELTA_PATCH_DONE: 00226 printf(_("success!\n")); 00227 break; 00228 case ALPM_EVENT_DELTA_PATCH_FAILED: 00229 printf(_("failed.\n")); 00230 break; 00231 case ALPM_EVENT_SCRIPTLET_INFO: 00232 fputs((const char *)data1, stdout); 00233 break; 00234 case ALPM_EVENT_RETRIEVE_START: 00235 printf(_(":: Retrieving packages ...\n")); 00236 break; 00237 case ALPM_EVENT_DISKSPACE_START: 00238 if(config->noprogressbar) { 00239 printf(_("checking available disk space...\n")); 00240 } 00241 break; 00242 /* all the simple done events, with fallthrough for each */ 00243 case ALPM_EVENT_FILECONFLICTS_DONE: 00244 case ALPM_EVENT_CHECKDEPS_DONE: 00245 case ALPM_EVENT_RESOLVEDEPS_DONE: 00246 case ALPM_EVENT_INTERCONFLICTS_DONE: 00247 case ALPM_EVENT_INTEGRITY_DONE: 00248 case ALPM_EVENT_LOAD_DONE: 00249 case ALPM_EVENT_DELTA_INTEGRITY_DONE: 00250 case ALPM_EVENT_DELTA_PATCHES_DONE: 00251 case ALPM_EVENT_DISKSPACE_DONE: 00252 /* nothing */ 00253 break; 00254 } 00255 fflush(stdout); 00256 } 00257 00258 /* callback to handle questions from libalpm transactions (yes/no) */ 00259 /* TODO this is one of the worst ever functions written. void *data ? wtf */ 00260 void cb_question(alpm_question_t event, void *data1, void *data2, 00261 void *data3, int *response) 00262 { 00263 if(config->print) { 00264 return; 00265 } 00266 switch(event) { 00267 case ALPM_QUESTION_INSTALL_IGNOREPKG: 00268 if(!config->op_s_downloadonly) { 00269 *response = yesno(_(":: %s is in IgnorePkg/IgnoreGroup. Install anyway?"), 00270 alpm_pkg_get_name(data1)); 00271 } else { 00272 *response = 1; 00273 } 00274 break; 00275 case ALPM_QUESTION_REPLACE_PKG: 00276 *response = yesno(_(":: Replace %s with %s/%s?"), 00277 alpm_pkg_get_name(data1), 00278 (char *)data3, 00279 alpm_pkg_get_name(data2)); 00280 break; 00281 case ALPM_QUESTION_CONFLICT_PKG: 00282 /* data parameters: target package, local package, conflict (strings) */ 00283 /* print conflict only if it contains new information */ 00284 if(strcmp(data1, data3) == 0 || strcmp(data2, data3) == 0) { 00285 *response = noyes(_(":: %s and %s are in conflict. Remove %s?"), 00286 (char *)data1, 00287 (char *)data2, 00288 (char *)data2); 00289 } else { 00290 *response = noyes(_(":: %s and %s are in conflict (%s). Remove %s?"), 00291 (char *)data1, 00292 (char *)data2, 00293 (char *)data3, 00294 (char *)data2); 00295 } 00296 break; 00297 case ALPM_QUESTION_REMOVE_PKGS: 00298 { 00299 alpm_list_t *unresolved = data1; 00300 alpm_list_t *namelist = NULL, *i; 00301 size_t count = 0; 00302 for(i = unresolved; i; i = i->next) { 00303 namelist = alpm_list_add(namelist, 00304 (char *)alpm_pkg_get_name(i->data)); 00305 count++; 00306 } 00307 printf(_n( 00308 ":: The following package cannot be upgraded due to unresolvable dependencies:\n", 00309 ":: The following packages cannot be upgraded due to unresolvable dependencies:\n", 00310 count)); 00311 list_display(" ", namelist); 00312 printf("\n"); 00313 *response = noyes(_n( 00314 "Do you want to skip the above package for this upgrade?", 00315 "Do you want to skip the above packages for this upgrade?", 00316 count)); 00317 alpm_list_free(namelist); 00318 } 00319 break; 00320 case ALPM_QUESTION_SELECT_PROVIDER: 00321 { 00322 alpm_list_t *providers = data1; 00323 size_t count = alpm_list_count(providers); 00324 char *depstring = alpm_dep_compute_string((alpm_depend_t *)data2); 00325 printf(_(":: There are %zd providers available for %s:\n"), count, 00326 depstring); 00327 free(depstring); 00328 select_display(providers); 00329 *response = select_question(count); 00330 } 00331 break; 00332 case ALPM_QUESTION_LOCAL_NEWER: 00333 if(!config->op_s_downloadonly) { 00334 *response = yesno(_(":: %s-%s: local version is newer. Upgrade anyway?"), 00335 alpm_pkg_get_name(data1), 00336 alpm_pkg_get_version(data1)); 00337 } else { 00338 *response = 1; 00339 } 00340 break; 00341 case ALPM_QUESTION_CORRUPTED_PKG: 00342 *response = yesno(_(":: File %s is corrupted (%s).\n" 00343 "Do you want to delete it?"), 00344 (char *)data1, 00345 alpm_strerror(*(alpm_errno_t *)data2)); 00346 break; 00347 case ALPM_QUESTION_IMPORT_KEY: 00348 { 00349 alpm_pgpkey_t *key = data1; 00350 char created[12]; 00351 const char *revoked = ""; 00352 time_t time = (time_t)key->created; 00353 strftime(created, 12, "%Y-%m-%d", localtime(&time)); 00354 00355 if(key->revoked) { 00356 revoked = " (revoked)"; 00357 } 00358 00359 *response = yesno(_(":: Import PGP key %d%c/%s, \"%s\", created: %s%s?"), 00360 key->length, key->pubkey_algo, key->fingerprint, key->uid, created, revoked); 00361 } 00362 break; 00363 } 00364 if(config->noask) { 00365 if(config->ask & event) { 00366 /* inverse the default answer */ 00367 *response = !*response; 00368 } 00369 } 00370 } 00371 00372 /* callback to handle display of transaction progress */ 00373 void cb_progress(alpm_progress_t event, const char *pkgname, int percent, 00374 size_t howmany, size_t current) 00375 { 00376 static int prevpercent; 00377 static size_t prevcurrent; 00378 /* size of line to allocate for text printing (e.g. not progressbar) */ 00379 int infolen; 00380 int digits, textlen; 00381 size_t tmp; 00382 char *opr = NULL; 00383 /* used for wide character width determination and printing */ 00384 int len, wclen, wcwid, padwid; 00385 wchar_t *wcstr; 00386 00387 const unsigned short cols = getcols(); 00388 00389 if(config->noprogressbar || cols == 0) { 00390 return; 00391 } 00392 00393 if(percent == 0) { 00394 get_update_timediff(1); 00395 } else if(percent == 100) { 00396 /* no need for timediff update, but unconditionally continue unless we 00397 * already completed on a previous call */ 00398 if(prevpercent == 100) { 00399 return; 00400 } 00401 } else { 00402 if(current != prevcurrent) { 00403 /* update always */ 00404 } else if(!pkgname || percent == prevpercent || 00405 get_update_timediff(0) < UPDATE_SPEED_MS) { 00406 /* only update the progress bar when we have a package name, the 00407 * percentage has changed, and it has been long enough. */ 00408 return; 00409 } 00410 } 00411 00412 prevpercent = percent; 00413 prevcurrent = current; 00414 00415 /* set text of message to display */ 00416 switch (event) { 00417 case ALPM_PROGRESS_ADD_START: 00418 opr = _("installing"); 00419 break; 00420 case ALPM_PROGRESS_UPGRADE_START: 00421 opr = _("upgrading"); 00422 break; 00423 case ALPM_PROGRESS_REMOVE_START: 00424 opr = _("removing"); 00425 break; 00426 case ALPM_PROGRESS_CONFLICTS_START: 00427 opr = _("checking for file conflicts"); 00428 break; 00429 case ALPM_PROGRESS_DISKSPACE_START: 00430 opr = _("checking available disk space"); 00431 break; 00432 case ALPM_PROGRESS_INTEGRITY_START: 00433 opr = _("checking package integrity"); 00434 break; 00435 case ALPM_PROGRESS_LOAD_START: 00436 opr = _("loading package files"); 00437 break; 00438 default: 00439 return; 00440 } 00441 00442 infolen = cols * 6 / 10; 00443 if(infolen < 50) { 00444 infolen = 50; 00445 } 00446 00447 /* find # of digits in package counts to scale output */ 00448 digits = 1; 00449 tmp = howmany; 00450 while((tmp /= 10)) { 00451 ++digits; 00452 } 00453 /* determine room left for non-digits text [not ( 1/12) part] */ 00454 textlen = infolen - 3 /* (/) */ - (2 * digits) - 1 /* space */; 00455 00456 /* In order to deal with characters from all locales, we have to worry 00457 * about wide characters and their column widths. A lot of stuff is 00458 * done here to figure out the actual number of screen columns used 00459 * by the output, and then pad it accordingly so we fill the terminal. 00460 */ 00461 /* len = opr len + pkgname len (if available) + space + null */ 00462 len = strlen(opr) + ((pkgname) ? strlen(pkgname) : 0) + 2; 00463 wcstr = calloc(len, sizeof(wchar_t)); 00464 /* print our strings to the alloc'ed memory */ 00465 #if defined(HAVE_SWPRINTF) 00466 wclen = swprintf(wcstr, len, L"%s %s", opr, pkgname); 00467 #else 00468 /* because the format string was simple, we can easily do this without 00469 * using swprintf, although it is probably not as safe/fast. The max 00470 * chars we can copy is decremented each time by subtracting the length 00471 * of the already printed/copied wide char string. */ 00472 wclen = mbstowcs(wcstr, opr, len); 00473 wclen += mbstowcs(wcstr + wclen, " ", len - wclen); 00474 wclen += mbstowcs(wcstr + wclen, pkgname, len - wclen); 00475 #endif 00476 wcwid = wcswidth(wcstr, wclen); 00477 padwid = textlen - wcwid; 00478 /* if padwid is < 0, we need to trim the string so padwid = 0 */ 00479 if(padwid < 0) { 00480 int i = textlen - 3; 00481 wchar_t *p = wcstr; 00482 /* grab the max number of char columns we can fill */ 00483 while(i > 0 && wcwidth(*p) < i) { 00484 i -= wcwidth(*p); 00485 p++; 00486 } 00487 /* then add the ellipsis and fill out any extra padding */ 00488 wcscpy(p, L"..."); 00489 padwid = i; 00490 00491 } 00492 00493 printf("(%*ld/%*ld) %ls%-*s", digits, (unsigned long)current, 00494 digits, (unsigned long)howmany, wcstr, padwid, ""); 00495 00496 free(wcstr); 00497 00498 /* call refactored fill progress function */ 00499 fill_progress(percent, percent, cols - infolen); 00500 00501 if(percent == 100) { 00502 alpm_list_t *i = NULL; 00503 on_progress = 0; 00504 for(i = output; i; i = i->next) { 00505 fputs((const char *)i->data, stdout); 00506 } 00507 fflush(stdout); 00508 FREELIST(output); 00509 } else { 00510 on_progress = 1; 00511 } 00512 } 00513 00514 /* callback to handle receipt of total download value */ 00515 void cb_dl_total(off_t total) 00516 { 00517 list_total = total; 00518 /* if we get a 0 value, it means this list has finished downloading, 00519 * so clear out our list_xfered as well */ 00520 if(total == 0) { 00521 list_xfered = 0; 00522 } 00523 } 00524 00525 /* callback to handle display of download progress */ 00526 void cb_dl_progress(const char *filename, off_t file_xfered, off_t file_total) 00527 { 00528 static double rate_last; 00529 static off_t xfered_last; 00530 static struct timeval initial_time; 00531 int infolen; 00532 int filenamelen; 00533 char *fname, *p; 00534 /* used for wide character width determination and printing */ 00535 int len, wclen, wcwid, padwid; 00536 wchar_t *wcfname; 00537 00538 int totaldownload = 0; 00539 off_t xfered, total; 00540 double rate = 0.0; 00541 long timediff = 0; 00542 unsigned int eta_h = 0, eta_m = 0, eta_s = 0; 00543 double rate_human, xfered_human; 00544 const char *rate_label, *xfered_label; 00545 int file_percent = 0, total_percent = 0; 00546 00547 const unsigned short cols = getcols(); 00548 00549 if(config->noprogressbar || cols == 0 || file_total == -1) { 00550 if(file_xfered == 0) { 00551 printf(_("downloading %s...\n"), filename); 00552 fflush(stdout); 00553 } 00554 return; 00555 } 00556 00557 infolen = cols * 6 / 10; 00558 if(infolen < 50) { 00559 infolen = 50; 00560 } 00561 /* only use TotalDownload if enabled and we have a callback value */ 00562 if(config->totaldownload && list_total) { 00563 /* sanity check */ 00564 if(list_xfered + file_total <= list_total) { 00565 totaldownload = 1; 00566 } else { 00567 /* bogus values : don't enable totaldownload and reset */ 00568 list_xfered = 0; 00569 list_total = 0; 00570 } 00571 } 00572 00573 if(totaldownload) { 00574 xfered = list_xfered + file_xfered; 00575 total = list_total; 00576 } else { 00577 xfered = file_xfered; 00578 total = file_total; 00579 } 00580 00581 /* bogus values : stop here */ 00582 if(xfered > total) { 00583 return; 00584 } 00585 00586 /* this is basically a switch on xfered: 0, total, and 00587 * anything else */ 00588 if(file_xfered == 0) { 00589 /* set default starting values, ensure we only call this once 00590 * if TotalDownload is enabled */ 00591 if(!totaldownload || (totaldownload && list_xfered == 0)) { 00592 gettimeofday(&initial_time, NULL); 00593 xfered_last = (off_t)0; 00594 rate_last = 0.0; 00595 get_update_timediff(1); 00596 } 00597 } else if(file_xfered == file_total) { 00598 /* compute final values */ 00599 struct timeval current_time; 00600 time_t diff_sec; 00601 suseconds_t diff_usec; 00602 00603 gettimeofday(¤t_time, NULL); 00604 diff_sec = current_time.tv_sec - initial_time.tv_sec; 00605 diff_usec = current_time.tv_usec - initial_time.tv_usec; 00606 timediff = (diff_sec * 1000) + (diff_usec / 1000); 00607 if(timediff > 0) { 00608 rate = (double)xfered / (timediff / 1000.0); 00609 /* round elapsed time (in ms) to the nearest second */ 00610 eta_s = (unsigned int)(timediff + 500) / 1000; 00611 } else { 00612 eta_s = 0; 00613 } 00614 } else { 00615 /* compute current average values */ 00616 timediff = get_update_timediff(0); 00617 00618 if(timediff < UPDATE_SPEED_MS) { 00619 /* return if the calling interval was too short */ 00620 return; 00621 } 00622 rate = (double)(xfered - xfered_last) / (timediff / 1000.0); 00623 /* average rate to reduce jumpiness */ 00624 rate = (rate + 2 * rate_last) / 3; 00625 if(rate > 0.0) { 00626 eta_s = (total - xfered) / rate; 00627 } else { 00628 eta_s = UINT_MAX; 00629 } 00630 rate_last = rate; 00631 xfered_last = xfered; 00632 } 00633 00634 if(file_total) { 00635 file_percent = (file_xfered * 100) / file_total; 00636 } else { 00637 file_percent = 100; 00638 } 00639 00640 if(totaldownload) { 00641 total_percent = ((list_xfered + file_xfered) * 100) / 00642 list_total; 00643 00644 /* if we are at the end, add the completed file to list_xfered */ 00645 if(file_xfered == file_total) { 00646 list_xfered += file_total; 00647 } 00648 } 00649 00650 /* fix up time for display */ 00651 eta_h = eta_s / 3600; 00652 eta_s -= eta_h * 3600; 00653 eta_m = eta_s / 60; 00654 eta_s -= eta_m * 60; 00655 00656 len = strlen(filename); 00657 fname = malloc(len + 1); 00658 memcpy(fname, filename, len); 00659 /* strip package or DB extension for cleaner look */ 00660 if((p = strstr(fname, ".pkg")) || (p = strstr(fname, ".db"))) { 00661 /* tack on a .sig suffix for signatures */ 00662 if(memcmp(&filename[len - 4], ".sig", 4) == 0) { 00663 memcpy(p, ".sig", 4); 00664 00665 /* adjust length for later calculations */ 00666 len = p - fname + 4; 00667 } else { 00668 len = p - fname; 00669 } 00670 } 00671 fname[len] = '\0'; 00672 00673 /* 1 space + filenamelen + 1 space + 6 for size + 1 space + 3 for label + 00674 * + 2 spaces + 4 for rate + 1 for label + 2 for /s + 1 space + 00675 * 8 for eta, gives us the magic 30 */ 00676 filenamelen = infolen - 30; 00677 /* see printf() code, we omit 'HH:' in these conditions */ 00678 if(eta_h == 0 || eta_h >= 100) { 00679 filenamelen += 3; 00680 } 00681 00682 /* In order to deal with characters from all locales, we have to worry 00683 * about wide characters and their column widths. A lot of stuff is 00684 * done here to figure out the actual number of screen columns used 00685 * by the output, and then pad it accordingly so we fill the terminal. 00686 */ 00687 /* len = filename len + null */ 00688 wcfname = calloc(len + 1, sizeof(wchar_t)); 00689 wclen = mbstowcs(wcfname, fname, len); 00690 wcwid = wcswidth(wcfname, wclen); 00691 padwid = filenamelen - wcwid; 00692 /* if padwid is < 0, we need to trim the string so padwid = 0 */ 00693 if(padwid < 0) { 00694 int i = filenamelen - 3; 00695 wchar_t *wcp = wcfname; 00696 /* grab the max number of char columns we can fill */ 00697 while(i > 0 && wcwidth(*wcp) < i) { 00698 i -= wcwidth(*wcp); 00699 wcp++; 00700 } 00701 /* then add the ellipsis and fill out any extra padding */ 00702 wcscpy(wcp, L"..."); 00703 padwid = i; 00704 00705 } 00706 00707 rate_human = humanize_size((off_t)rate, '\0', &rate_label); 00708 xfered_human = humanize_size(xfered, '\0', &xfered_label); 00709 00710 printf(" %ls%-*s ", wcfname, padwid, ""); 00711 /* We will show 1.62M/s, 11.6M/s, but 116K/s and 1116K/s */ 00712 if(rate_human < 9.995) { 00713 printf("%6.1f %3s %4.2f%c/s ", 00714 xfered_human, xfered_label, rate_human, rate_label[0]); 00715 } else if(rate_human < 99.95) { 00716 printf("%6.1f %3s %4.1f%c/s ", 00717 xfered_human, xfered_label, rate_human, rate_label[0]); 00718 } else { 00719 printf("%6.1f %3s %4.f%c/s ", 00720 xfered_human, xfered_label, rate_human, rate_label[0]); 00721 } 00722 if(eta_h == 0) { 00723 printf("%02u:%02u", eta_m, eta_s); 00724 } else if(eta_h < 100) { 00725 printf("%02u:%02u:%02u", eta_h, eta_m, eta_s); 00726 } else { 00727 fputs("--:--", stdout); 00728 } 00729 00730 free(fname); 00731 free(wcfname); 00732 00733 if(totaldownload) { 00734 fill_progress(file_percent, total_percent, cols - infolen); 00735 } else { 00736 fill_progress(file_percent, file_percent, cols - infolen); 00737 } 00738 return; 00739 } 00740 00741 /* Callback to handle notifications from the library */ 00742 void cb_log(alpm_loglevel_t level, const char *fmt, va_list args) 00743 { 00744 if(!fmt || strlen(fmt) == 0) { 00745 return; 00746 } 00747 00748 if(on_progress) { 00749 char *string = NULL; 00750 pm_vasprintf(&string, level, fmt, args); 00751 if(string != NULL) { 00752 output = alpm_list_add(output, string); 00753 } 00754 } else { 00755 pm_vfprintf(stderr, level, fmt, args); 00756 } 00757 } 00758 00759 /* vim: set ts=2 sw=2 noet: */