callback.c

Go to the documentation of this file.
00001 /*
00002  *  callback.c
00003  *
00004  *  Copyright (c) 2002-2007 by Judd Vinet <jvinet@zeroflux.org>
00005  *
00006  *  This program is free software; you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License as published by
00008  *  the Free Software Foundation; either version 2 of the License, or
00009  *  (at your option) any later version.
00010  *
00011  *  This program is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  *  GNU General Public License for more details.
00015  *
00016  *  You should have received a copy of the GNU General Public License
00017  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
00018  */
00019 
00020 #include "config.h"
00021 
00022 #include <stdio.h>
00023 #include <stdlib.h>
00024 #include <string.h>
00025 #include <sys/time.h>
00026 #include <unistd.h>
00027 #include <dirent.h>
00028 #include <wchar.h>
00029 
00030 #include <alpm.h>
00031 
00032 /* pacman */
00033 #include "callback.h"
00034 #include "util.h"
00035 #include "conf.h"
00036 
00037 /* TODO this should not have to be defined twice- trans.c & log.c */
00038 #define LOG_STR_LEN 256
00039 #define FILENAME_TRIM_LEN 23
00040 
00041 /* download progress bar */
00042 static float rate_last;
00043 static int xfered_last;
00044 static struct timeval initial_time;
00045 
00046 /* transaction progress bar */
00047 static int prevpercent = 0; /* for less progressbar output */
00048 
00049 /* delayed output during progress bar */
00050 static int on_progress = 0;
00051 static alpm_list_t *output = NULL;
00052 
00053 /* Silly little helper function, determines if the caller needs a visual update
00054  * since the last time this function was called.
00055  * This is made for the two progress bar functions, to prevent flicker
00056  *
00057  * first_call indicates if this is the first time it is called, for
00058  * initialization purposes */
00059 static float get_update_timediff(int first_call)
00060 {
00061     float retval = 0.0;
00062     static struct timeval last_time = {0, 0};
00063 
00064     /* on first call, simply set the last time and return */
00065     if(first_call) {
00066         gettimeofday(&last_time, NULL);
00067     } else {
00068         struct timeval this_time;
00069         float diff_sec, diff_usec;
00070 
00071         gettimeofday(&this_time, NULL);
00072         diff_sec = this_time.tv_sec - last_time.tv_sec;
00073         diff_usec = this_time.tv_usec - last_time.tv_usec;
00074 
00075         retval = diff_sec + (diff_usec / 1000000.0);
00076 
00077         /* return 0 and do not update last_time if interval was too short */
00078         if(retval < UPDATE_SPEED_SEC) {
00079             retval = 0.0;
00080         } else {
00081             last_time = this_time;
00082             /* printf("\nupdate retval: %f\n", retval); DEBUG*/
00083         }
00084     }
00085 
00086     return(retval);
00087 }
00088 
00089 /* refactored from cb_trans_progress */
00090 static void fill_progress(const int graph_percent, const int display_percent,
00091         const int proglen)
00092 {
00093     const unsigned int hashlen = proglen - 8;
00094     const unsigned int hash = graph_percent * hashlen / 100;
00095     static unsigned int lasthash = 0, mouth = 0;
00096     unsigned int i;
00097 
00098     /* printf("\ndebug: proglen: %i\n", proglen); DEBUG*/
00099 
00100     if(graph_percent == 0) {
00101         lasthash = 0;
00102         mouth = 0;
00103     }
00104 
00105     /* magic numbers, how I loathe thee */
00106     if(proglen > 8) {
00107         printf(" [");
00108         for(i = hashlen; i > 1; --i) {
00109             /* if special progress bar enabled */
00110             if(config->chomp) {
00111                 if(i > hashlen - hash) {
00112                     printf("-");
00113                 } else if(i == hashlen - hash) {
00114                     if(lasthash == hash) {
00115                         if(mouth) {
00116                             printf("\033[1;33mC\033[m");
00117                         } else {
00118                             printf("\033[1;33mc\033[m");
00119                         }
00120                     } else {
00121                         lasthash = hash;
00122                         mouth = mouth == 1 ? 0 : 1;
00123                         if(mouth) {
00124                             printf("\033[1;33mC\033[m");
00125                         } else {
00126                             printf("\033[1;33mc\033[m");
00127                         }
00128                     }
00129                 } else if(i%3 == 0) {
00130                     printf("\033[0;37mo\033[m");
00131                 } else {
00132                     printf("\033[0;37m \033[m");
00133                 }
00134             } /* else regular progress bar */
00135             else if(i > hashlen - hash) {
00136                 printf("#");
00137             } else {
00138                 printf("-");
00139             }
00140         }
00141         printf("]");
00142     }
00143     /* print percent after progress bar */
00144     if(proglen > 5) {
00145         printf(" %3d%%", display_percent);
00146     }
00147 
00148     if(graph_percent == 100) {
00149         printf("\n");
00150     } else {
00151         printf("\r");
00152     }
00153     fflush(stdout);
00154 }
00155 
00156 
00157 
00158 /* callback to handle messages/notifications from libalpm transactions */
00159 void cb_trans_evt(pmtransevt_t event, void *data1, void *data2)
00160 {
00161     char str[LOG_STR_LEN] = "";
00162 
00163     switch(event) {
00164         case PM_TRANS_EVT_CHECKDEPS_START:
00165           printf(_("checking dependencies...\n"));
00166             break;
00167         case PM_TRANS_EVT_FILECONFLICTS_START:
00168             if(config->noprogressbar) {
00169             printf(_("checking for file conflicts...\n"));
00170             }
00171             break;
00172         case PM_TRANS_EVT_RESOLVEDEPS_START:
00173             printf(_("resolving dependencies...\n"));
00174             break;
00175         case PM_TRANS_EVT_INTERCONFLICTS_START:
00176             printf(_("looking for inter-conflicts...\n"));
00177             break;
00178         case PM_TRANS_EVT_ADD_START:
00179             if(config->noprogressbar) {
00180                 printf(_("installing %s...\n"), alpm_pkg_get_name(data1));
00181             }
00182             break;
00183         case PM_TRANS_EVT_ADD_DONE:
00184             snprintf(str, LOG_STR_LEN, "installed %s (%s)\n",
00185                      alpm_pkg_get_name(data1),
00186                      alpm_pkg_get_version(data1));
00187             alpm_logaction(str);
00188             break;
00189         case PM_TRANS_EVT_REMOVE_START:
00190             if(config->noprogressbar) {
00191             printf(_("removing %s...\n"), alpm_pkg_get_name(data1));
00192             }
00193             break;
00194         case PM_TRANS_EVT_REMOVE_DONE:
00195             snprintf(str, LOG_STR_LEN, "removed %s (%s)\n",
00196                      alpm_pkg_get_name(data1),
00197                      alpm_pkg_get_version(data1));
00198             alpm_logaction(str);
00199             break;
00200         case PM_TRANS_EVT_UPGRADE_START:
00201             if(config->noprogressbar) {
00202                 printf(_("upgrading %s...\n"), alpm_pkg_get_name(data1));
00203             }
00204             break;
00205         case PM_TRANS_EVT_UPGRADE_DONE:
00206             snprintf(str, LOG_STR_LEN, "upgraded %s (%s -> %s)\n",
00207                      (char *)alpm_pkg_get_name(data1),
00208                      (char *)alpm_pkg_get_version(data2),
00209                      (char *)alpm_pkg_get_version(data1));
00210             alpm_logaction(str);
00211             break;
00212         case PM_TRANS_EVT_INTEGRITY_START:
00213             printf(_("checking package integrity...\n"));
00214             break;
00215         case PM_TRANS_EVT_DELTA_INTEGRITY_START:
00216             printf(_("checking delta integrity...\n"));
00217             break;
00218         case PM_TRANS_EVT_DELTA_PATCHES_START:
00219             printf(_("applying deltas...\n"));
00220             break;
00221         case PM_TRANS_EVT_DELTA_PATCH_START:
00222             printf(_("generating %s with %s... "), (char *)data1, (char *)data2);
00223             break;
00224         case PM_TRANS_EVT_DELTA_PATCH_DONE:
00225             printf(_("success!\n"));
00226             break;
00227         case PM_TRANS_EVT_DELTA_PATCH_FAILED:
00228             printf(_("failed.\n"));
00229             break;
00230         case PM_TRANS_EVT_SCRIPTLET_INFO:
00231             printf("%s", (char*)data1);
00232             break;
00233         case PM_TRANS_EVT_PRINTURI:
00234             printf("%s/%s\n", (char*)data1, (char*)data2);
00235             break;
00236         case PM_TRANS_EVT_RETRIEVE_START:
00237             printf(_(":: Retrieving packages from %s...\n"), (char*)data1);
00238             break;
00239         /* all the simple done events, with fallthrough for each */
00240         case PM_TRANS_EVT_FILECONFLICTS_DONE:
00241         case PM_TRANS_EVT_EXTRACT_DONE:
00242         case PM_TRANS_EVT_CHECKDEPS_DONE:
00243         case PM_TRANS_EVT_RESOLVEDEPS_DONE:
00244         case PM_TRANS_EVT_INTERCONFLICTS_DONE:
00245         case PM_TRANS_EVT_INTEGRITY_DONE:
00246         case PM_TRANS_EVT_DELTA_INTEGRITY_DONE:
00247         case PM_TRANS_EVT_DELTA_PATCHES_DONE:
00248             /* nothing */
00249             break;
00250     }
00251     fflush(stdout);
00252 }
00253 
00254 /* callback to handle questions from libalpm transactions (yes/no) */
00255 /* TODO this is one of the worst ever functions written. void *data ? wtf */
00256 void cb_trans_conv(pmtransconv_t event, void *data1, void *data2,
00257                    void *data3, int *response)
00258 {
00259     char str[LOG_STR_LEN] = "";
00260 
00261     switch(event) {
00262         case PM_TRANS_CONV_INSTALL_IGNOREPKG:
00263             if(data2) {
00264                 /* TODO we take this route based on data2 being not null? WTF */
00265                 snprintf(str, LOG_STR_LEN, _(":: %s requires installing %s from IgnorePkg/IgnoreGroup. Install anyway? [Y/n] "),
00266                         alpm_pkg_get_name(data1),
00267                         alpm_pkg_get_name(data2));
00268                 *response = yesno(str);
00269             } else {
00270                 snprintf(str, LOG_STR_LEN, _(":: %s is in IgnorePkg/IgnoreGroup. Install anyway? [Y/n] "),
00271                         alpm_pkg_get_name(data1));
00272                 *response = yesno(str);
00273             }
00274             break;
00275         case PM_TRANS_CONV_REMOVE_HOLDPKG:
00276             snprintf(str, LOG_STR_LEN, _(":: %s is designated as a HoldPkg. Remove anyway? [Y/n] "),
00277                     alpm_pkg_get_name(data1));
00278             *response = yesno(str);
00279             break;
00280         case PM_TRANS_CONV_REPLACE_PKG:
00281             if(!config->noconfirm) {
00282                 snprintf(str, LOG_STR_LEN, _(":: Replace %s with %s/%s? [Y/n] "),
00283                         alpm_pkg_get_name(data1),
00284                         (char *)data3,
00285                         alpm_pkg_get_name(data2));
00286                 *response = yesno(str);
00287             } else {
00288                 printf(_("Replacing %s with %s/%s\n."),
00289                         alpm_pkg_get_name(data1),
00290                         (char *)data3,
00291                         alpm_pkg_get_name(data2));
00292                 *response = 1;
00293             }
00294             break;
00295         case PM_TRANS_CONV_CONFLICT_PKG:
00296             snprintf(str, LOG_STR_LEN, _(":: %s conflicts with %s. Remove %s? [Y/n] "),
00297                     (char *)data1,
00298                     (char *)data2,
00299                     (char *)data2);
00300             *response = yesno(str);
00301             break;
00302         case PM_TRANS_CONV_LOCAL_NEWER:
00303             if(!config->op_s_downloadonly) {
00304                 snprintf(str, LOG_STR_LEN, _(":: %s-%s: local version is newer. Upgrade anyway? [Y/n] "),
00305                         alpm_pkg_get_name(data1),
00306                         alpm_pkg_get_version(data1));
00307                 *response = yesno(str);
00308             } else {
00309                 *response = 1;
00310             }
00311             break;
00312         case PM_TRANS_CONV_CORRUPTED_PKG:
00313             if(!config->noconfirm) {
00314                 snprintf(str, LOG_STR_LEN, _(":: File %s is corrupted. Do you want to delete it? [Y/n] "),
00315                         (char *)data1);
00316                 *response = yesno(str);
00317             } else {
00318                 *response = 1;
00319             }
00320             break;
00321     }
00322 }
00323 
00324 /* callback to handle display of transaction progress */
00325 void cb_trans_progress(pmtransprog_t event, const char *pkgname, int percent,
00326                        int howmany, int remain)
00327 {
00328     float timediff;
00329 
00330     /* size of line to allocate for text printing (e.g. not progressbar) */
00331     const int infolen = 50;
00332     int tmp, digits, oprlen, textlen, pkglen;
00333     char *opr = NULL;
00334     wchar_t *wcopr = NULL;
00335 
00336     if(config->noprogressbar) {
00337         return;
00338     }
00339 
00340     if(percent == 0) {
00341         timediff = get_update_timediff(1);
00342     } else {
00343         timediff = get_update_timediff(0);
00344     }
00345 
00346     if(percent > 0 && percent < 100 && !timediff) {
00347         /* only update the progress bar when
00348          * a) we first start
00349          * b) we end the progress
00350          * c) it has been long enough since the last call
00351          */
00352         return;
00353     }
00354 
00355     /* if no pkgname, percent is too high or unchanged, then return */
00356     if(!pkgname || percent == prevpercent) {
00357         return;
00358     }
00359 
00360     prevpercent=percent;
00361     /* set text of message to display */
00362     switch (event) {
00363         case PM_TRANS_PROGRESS_ADD_START:
00364             opr = _("installing");
00365             break;
00366         case PM_TRANS_PROGRESS_UPGRADE_START:
00367             opr = _("upgrading");
00368             break;
00369         case PM_TRANS_PROGRESS_REMOVE_START:
00370             opr = _("removing");
00371             break;
00372         case PM_TRANS_PROGRESS_CONFLICTS_START:
00373             opr = _("checking for file conflicts");
00374             break;
00375     }
00376     /* convert above strings to wide chars */
00377     oprlen = strlen(opr);
00378     wcopr = calloc(oprlen, sizeof(wchar_t));
00379     if(!wcopr) {
00380         fprintf(stderr, "malloc failure: could not allocate %zd bytes\n",
00381                 strlen(opr) * sizeof(wchar_t));
00382         return;
00383     }
00384     oprlen = mbstowcs(wcopr, opr, oprlen);
00385 
00386     /* find # of digits in package counts to scale output */
00387     digits = 1;
00388     tmp = howmany;
00389     while((tmp /= 10)) {
00390         ++digits;
00391     }
00392 
00393     /* determine room left for non-digits text [not ( 1/12) part] */
00394     textlen = infolen - 3 - (2 * digits);
00395     /* room left for package name */
00396     pkglen = textlen - oprlen - 1;
00397 
00398     switch (event) {
00399         case PM_TRANS_PROGRESS_ADD_START:
00400         case PM_TRANS_PROGRESS_UPGRADE_START:
00401         case PM_TRANS_PROGRESS_REMOVE_START:
00402             /* old way of doing it, but ISO C does not recognize it
00403             printf("(%2$*1$d/%3$*1$d) %4$s %6$-*5$.*5$s", digits, remain, howmany,
00404                    opr, pkglen, pkgname);*/
00405             printf("(%*d/%*d) %s %-*.*s", digits, remain, digits, howmany,
00406                    opr, pkglen, pkglen, pkgname);
00407             break;
00408         case PM_TRANS_PROGRESS_CONFLICTS_START:
00409             /* old way of doing it, but ISO C does not recognize it
00410             printf("(%2$*1$d/%3$*1$d) %5$-*4$s", digits, remain, howmany,
00411                    textlen, opr);*/
00412             printf("(%*d/%*d) %-*s", digits, remain, digits, howmany,
00413                    textlen, opr);
00414             break;
00415     }
00416 
00417     free(wcopr);
00418 
00419     /* call refactored fill progress function */
00420     fill_progress(percent, percent, getcols() - infolen);
00421 
00422     if(percent == 100) {
00423         alpm_list_t *i = NULL;
00424         on_progress = 0;
00425         for(i = output; i; i = i->next) {
00426             printf("%s", (char *)i->data);
00427         }
00428         fflush(stdout);
00429         FREELIST(output);
00430     } else {
00431         on_progress = 1;
00432     }
00433 }
00434 
00435 /* callback to handle display of download progress */
00436 void cb_dl_progress(const char *filename, int file_xfered, int file_total,
00437         int list_xfered, int list_total)
00438 {
00439     const int infolen = 50;
00440     char *fname, *p;
00441 
00442     float rate = 0.0, timediff = 0.0, f_xfered = 0.0;
00443     unsigned int eta_h = 0, eta_m = 0, eta_s = 0;
00444     int graph_percent = 0, display_percent = 0;
00445     char rate_size = 'K', xfered_size = 'K';
00446     int xfered = 0, total = 0;
00447 
00448     /* Need this variable when TotalDownload is set to know if we should
00449      * reset xfered_last and rate_last. */
00450     static int has_init = 0;
00451 
00452     if(config->noprogressbar) {
00453         return;
00454     }
00455 
00456     /* Choose how to display the amount downloaded, rate, ETA, and
00457      * percentage depending on the TotalDownload option. */
00458     if (config->totaldownload && list_total > 0) {
00459         xfered = list_xfered;
00460         total = list_total;
00461     } else {
00462         xfered = file_xfered;
00463         total = file_total;
00464     }
00465 
00466     /* this is basically a switch on file_xferred: 0, file_total, and
00467      * anything else */
00468     if(file_xfered == 0) {
00469         /* set default starting values, but only once for TotalDownload */
00470         if (!(config->totaldownload && list_total > 0) ||
00471                 (config->totaldownload && list_total > 0 && !has_init)) {
00472             gettimeofday(&initial_time, NULL);
00473             timediff = get_update_timediff(1);
00474             xfered_last = 0;
00475             rate_last = 0.0;
00476             has_init = 1;
00477         }
00478         rate = 0.0;
00479         eta_s = 0;
00480     } else if(file_xfered == file_total) {
00481         /* compute final values */
00482         struct timeval current_time;
00483         float diff_sec, diff_usec;
00484 
00485         gettimeofday(&current_time, NULL);
00486         diff_sec = current_time.tv_sec - initial_time.tv_sec;
00487         diff_usec = current_time.tv_usec - initial_time.tv_usec;
00488         timediff = diff_sec + (diff_usec / 1000000.0);
00489         rate = xfered / (timediff * 1024.0);
00490 
00491         /* round elapsed time to the nearest second */
00492         eta_s = (int)(timediff + 0.5);
00493     } else {
00494         /* compute current average values */
00495         timediff = get_update_timediff(0);
00496 
00497         if(timediff < UPDATE_SPEED_SEC) {
00498             /* return if the calling interval was too short */
00499             return;
00500         }
00501         rate = (xfered - xfered_last) / (timediff * 1024.0);
00502         /* average rate to reduce jumpiness */
00503         rate = (rate + 2*rate_last) / 3;
00504         eta_s = (total - xfered) / (rate * 1024.0);
00505         rate_last = rate;
00506         xfered_last = xfered;
00507     }
00508 
00509     /* fix up time for display */
00510     eta_h = eta_s / 3600;
00511     eta_s -= eta_h * 3600;
00512     eta_m = eta_s / 60;
00513     eta_s -= eta_m * 60;
00514 
00515     fname = strdup(filename);
00516     /* strip package or DB extension for cleaner look */
00517     if((p = strstr(fname, PKGEXT)) || (p = strstr(fname, DBEXT))) {
00518             *p = '\0';
00519     }
00520     if(strlen(fname) > FILENAME_TRIM_LEN) {
00521         strcpy(fname + FILENAME_TRIM_LEN -3,"...");
00522     }
00523 
00524     /* Awesome formatting for progress bar.  We need a mess of Kb->Mb->Gb stuff
00525      * here. We'll use limit of 2048 for each until we get some empirical */
00526     /* rate_size = 'K'; was set above */
00527     if(rate > 2048.0) {
00528         rate /= 1024.0;
00529         rate_size = 'M';
00530         if(rate > 2048.0) {
00531             rate /= 1024.0;
00532             rate_size = 'G';
00533             /* we should not go higher than this for a few years (9999.9 Gb/s?)*/
00534         }
00535     }
00536 
00537     f_xfered = xfered / 1024.0; /* convert to K by default */
00538     /* xfered_size = 'K'; was set above */
00539     if(f_xfered > 2048.0) {
00540         f_xfered /= 1024.0;
00541         xfered_size = 'M';
00542         if(f_xfered > 2048.0) {
00543             f_xfered /= 1024.0;
00544             xfered_size = 'G';
00545             /* I should seriously hope that archlinux packages never break
00546              * the 9999.9GB mark... we'd have more serious problems than the progress
00547              * bar in pacman */
00548         }
00549     }
00550 
00551     printf(" %-*s %6.1f%c %#6.1f%c/s %02u:%02u:%02u", FILENAME_TRIM_LEN, fname,
00552                  f_xfered, xfered_size, rate, rate_size, eta_h, eta_m, eta_s);
00553 
00554     free(fname);
00555 
00556     /* The progress bar is based on the file percent regardless of the
00557      * TotalDownload option. */
00558     graph_percent = (int)((float)file_xfered) / ((float)file_total) * 100;
00559     display_percent = (int)((float)xfered) / ((float)total) * 100;
00560     fill_progress(graph_percent, display_percent, getcols() - infolen);
00561     return;
00562 }
00563 
00564 /* Callback to handle notifications from the library */
00565 void cb_log(pmloglevel_t level, char *fmt, va_list args)
00566 {
00567     if(!strlen(fmt)) {
00568         return;
00569     }
00570 
00571     if(on_progress) {
00572         char *string = NULL;
00573         pm_vasprintf(&string, level, fmt, args);
00574         if(string != NULL) {
00575             output = alpm_list_add(output, string);
00576         }
00577     } else {
00578         pm_vfprintf(stdout, level, fmt, args);
00579     }
00580 }
00581 
00582 /* vim: set ts=2 sw=2 noet: */

Generated on Mon Jan 14 23:53:39 2008 for libalpm by  doxygen 1.5.4