libalpm
Arch Linux Package Manager Library
util.c
Go to the documentation of this file.
00001 /*
00002  *  util.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 <sys/types.h>
00022 #include <sys/ioctl.h>
00023 #include <sys/stat.h>
00024 #include <time.h>
00025 
00026 #include <stdio.h>
00027 #include <stdlib.h>
00028 #include <stdarg.h>
00029 #include <stdint.h> /* intmax_t */
00030 #include <string.h>
00031 #include <errno.h>
00032 #include <ctype.h>
00033 #include <dirent.h>
00034 #include <unistd.h>
00035 #include <limits.h>
00036 #include <wchar.h>
00037 #ifdef HAVE_TERMIOS_H
00038 #include <termios.h> /* tcflush */
00039 #endif
00040 
00041 #include <alpm.h>
00042 #include <alpm_list.h>
00043 
00044 /* pacman */
00045 #include "util.h"
00046 #include "conf.h"
00047 #include "callback.h"
00048 
00049 
00050 int trans_init(alpm_transflag_t flags, int check_valid)
00051 {
00052     int ret;
00053 
00054     check_syncdbs(0, check_valid);
00055 
00056     ret = alpm_trans_init(config->handle, flags);
00057     if(ret == -1) {
00058         trans_init_error();
00059         return -1;
00060     }
00061     return 0;
00062 }
00063 
00064 void trans_init_error(void)
00065 {
00066     alpm_errno_t err = alpm_errno(config->handle);
00067     pm_printf(ALPM_LOG_ERROR, _("failed to init transaction (%s)\n"),
00068             alpm_strerror(err));
00069     if(err == ALPM_ERR_HANDLE_LOCK) {
00070         fprintf(stderr, _("  if you're sure a package manager is not already\n"
00071                     "  running, you can remove %s\n"),
00072                 alpm_option_get_lockfile(config->handle));
00073     }
00074 }
00075 
00076 int trans_release(void)
00077 {
00078     if(alpm_trans_release(config->handle) == -1) {
00079         pm_printf(ALPM_LOG_ERROR, _("failed to release transaction (%s)\n"),
00080                 alpm_strerror(alpm_errno(config->handle)));
00081         return -1;
00082     }
00083     return 0;
00084 }
00085 
00086 int needs_root(void)
00087 {
00088     switch(config->op) {
00089         case PM_OP_DATABASE:
00090             return 1;
00091         case PM_OP_UPGRADE:
00092         case PM_OP_REMOVE:
00093             return !config->print;
00094         case PM_OP_SYNC:
00095             return (config->op_s_clean || config->op_s_sync ||
00096                     (!config->group && !config->op_s_info && !config->op_q_list &&
00097                      !config->op_s_search && !config->print));
00098         default:
00099             return 0;
00100     }
00101 }
00102 
00103 int check_syncdbs(size_t need_repos, int check_valid)
00104 {
00105     int ret = 0;
00106     alpm_list_t *i;
00107     alpm_list_t *sync_dbs = alpm_option_get_syncdbs(config->handle);
00108 
00109     if(need_repos && sync_dbs == NULL) {
00110         pm_printf(ALPM_LOG_ERROR, _("no usable package repositories configured.\n"));
00111         return 1;
00112     }
00113 
00114     if(check_valid) {
00115         /* ensure all known dbs are valid */
00116         for(i = sync_dbs; i; i = alpm_list_next(i)) {
00117             alpm_db_t *db = i->data;
00118             if(alpm_db_get_valid(db)) {
00119                 pm_printf(ALPM_LOG_ERROR, _("database '%s' is not valid (%s)\n"),
00120                         alpm_db_get_name(db), alpm_strerror(alpm_errno(config->handle)));
00121                 ret = 1;
00122             }
00123         }
00124     }
00125     return ret;
00126 }
00127 
00128 /* discard unhandled input on the terminal's input buffer */
00129 static int flush_term_input(void) {
00130 #ifdef HAVE_TCFLUSH
00131     if(isatty(fileno(stdin))) {
00132         return tcflush(fileno(stdin), TCIFLUSH);
00133     }
00134 #endif
00135 
00136     /* fail silently */
00137     return 0;
00138 }
00139 
00140 /* gets the current screen column width */
00141 unsigned short getcols(void)
00142 {
00143     const unsigned short default_tty = 80;
00144     const unsigned short default_notty = 0;
00145     unsigned short termwidth = 0;
00146 
00147     if(!isatty(fileno(stdout))) {
00148         return default_notty;
00149     }
00150 
00151 #ifdef TIOCGSIZE
00152     struct ttysize win;
00153     if(ioctl(1, TIOCGSIZE, &win) == 0) {
00154         termwidth = win.ts_cols;
00155     }
00156 #elif defined(TIOCGWINSZ)
00157     struct winsize win;
00158     if(ioctl(1, TIOCGWINSZ, &win) == 0) {
00159         termwidth = win.ws_col;
00160     }
00161 #endif
00162     return termwidth == 0 ? default_tty : termwidth;
00163 }
00164 
00165 /* does the same thing as 'rm -rf' */
00166 int rmrf(const char *path)
00167 {
00168     int errflag = 0;
00169     struct dirent *dp;
00170     DIR *dirp;
00171 
00172     if(!unlink(path)) {
00173         return 0;
00174     } else {
00175         if(errno == ENOENT) {
00176             return 0;
00177         } else if(errno == EPERM) {
00178             /* fallthrough */
00179         } else if(errno == EISDIR) {
00180             /* fallthrough */
00181         } else if(errno == ENOTDIR) {
00182             return 1;
00183         } else {
00184             /* not a directory */
00185             return 1;
00186         }
00187 
00188         dirp = opendir(path);
00189         if(!dirp) {
00190             return 1;
00191         }
00192         for(dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
00193             if(dp->d_ino) {
00194                 char name[PATH_MAX];
00195                 sprintf(name, "%s/%s", path, dp->d_name);
00196                 if(strcmp(dp->d_name, "..") != 0 && strcmp(dp->d_name, ".") != 0) {
00197                     errflag += rmrf(name);
00198                 }
00199             }
00200         }
00201         closedir(dirp);
00202         if(rmdir(path)) {
00203             errflag++;
00204         }
00205         return errflag;
00206     }
00207 }
00208 
00209 /** Parse the basename of a program from a path.
00210 * @param path path to parse basename from
00211 *
00212 * @return everything following the final '/'
00213 */
00214 const char *mbasename(const char *path)
00215 {
00216     const char *last = strrchr(path, '/');
00217     if(last) {
00218         return last + 1;
00219     }
00220     return path;
00221 }
00222 
00223 /** Parse the dirname of a program from a path.
00224 * The path returned should be freed.
00225 * @param path path to parse dirname from
00226 *
00227 * @return everything preceding the final '/'
00228 */
00229 char *mdirname(const char *path)
00230 {
00231     char *ret, *last;
00232 
00233     /* null or empty path */
00234     if(path == NULL || path == '\0') {
00235         return strdup(".");
00236     }
00237 
00238     ret = strdup(path);
00239     last = strrchr(ret, '/');
00240 
00241     if(last != NULL) {
00242         /* we found a '/', so terminate our string */
00243         *last = '\0';
00244         return ret;
00245     }
00246     /* no slash found */
00247     free(ret);
00248     return strdup(".");
00249 }
00250 
00251 /* output a string, but wrap words properly with a specified indentation
00252  */
00253 void indentprint(const char *str, size_t indent)
00254 {
00255     wchar_t *wcstr;
00256     const wchar_t *p;
00257     int len, cidx;
00258     const unsigned short cols = getcols();
00259 
00260     if(!str) {
00261         return;
00262     }
00263 
00264     /* if we're not a tty, or our tty is not wide enough that wrapping even makes
00265      * sense, print without indenting */
00266     if(cols == 0 || indent > cols) {
00267         fputs(str, stdout);
00268         return;
00269     }
00270 
00271     len = strlen(str) + 1;
00272     wcstr = calloc(len, sizeof(wchar_t));
00273     len = mbstowcs(wcstr, str, len);
00274     p = wcstr;
00275     cidx = indent;
00276 
00277     if(!p || !len) {
00278         return;
00279     }
00280 
00281     while(*p) {
00282         if(*p == L' ') {
00283             const wchar_t *q, *next;
00284             p++;
00285             if(p == NULL || *p == L' ') continue;
00286             next = wcschr(p, L' ');
00287             if(next == NULL) {
00288                 next = p + wcslen(p);
00289             }
00290             /* len captures # cols */
00291             len = 0;
00292             q = p;
00293             while(q < next) {
00294                 len += wcwidth(*q++);
00295             }
00296             if(len > (cols - cidx - 1)) {
00297                 /* wrap to a newline and reindent */
00298                 printf("\n%-*s", (int)indent, "");
00299                 cidx = indent;
00300             } else {
00301                 printf(" ");
00302                 cidx++;
00303             }
00304             continue;
00305         }
00306         printf("%lc", (wint_t)*p);
00307         cidx += wcwidth(*p);
00308         p++;
00309     }
00310     free(wcstr);
00311 }
00312 
00313 /* Convert a string to uppercase
00314  */
00315 char *strtoupper(char *str)
00316 {
00317     char *ptr = str;
00318 
00319     while(*ptr) {
00320         (*ptr) = (char)toupper((unsigned char)*ptr);
00321         ptr++;
00322     }
00323     return str;
00324 }
00325 
00326 /* Trim whitespace and newlines from a string
00327  */
00328 size_t strtrim(char *str)
00329 {
00330     char *end, *pch = str;
00331 
00332     if(str == NULL || *str == '\0') {
00333         /* string is empty, so we're done. */
00334         return 0;
00335     }
00336 
00337     while(isspace((unsigned char)*pch)) {
00338         pch++;
00339     }
00340     if(pch != str) {
00341         size_t len = strlen(pch);
00342         if(len) {
00343             memmove(str, pch, len + 1);
00344         } else {
00345             *str = '\0';
00346         }
00347     }
00348 
00349     /* check if there wasn't anything but whitespace in the string. */
00350     if(*str == '\0') {
00351         return 0;
00352     }
00353 
00354     end = (str + strlen(str) - 1);
00355     while(isspace((unsigned char)*end)) {
00356         end--;
00357     }
00358     *++end = '\0';
00359 
00360     return end - pch;
00361 }
00362 
00363 /* Replace all occurances of 'needle' with 'replace' in 'str', returning
00364  * a new string (must be free'd) */
00365 char *strreplace(const char *str, const char *needle, const char *replace)
00366 {
00367     const char *p = NULL, *q = NULL;
00368     char *newstr = NULL, *newp = NULL;
00369     alpm_list_t *i = NULL, *list = NULL;
00370     size_t needlesz = strlen(needle), replacesz = strlen(replace);
00371     size_t newsz;
00372 
00373     if(!str) {
00374         return NULL;
00375     }
00376 
00377     p = str;
00378     q = strstr(p, needle);
00379     while(q) {
00380         list = alpm_list_add(list, (char *)q);
00381         p = q + needlesz;
00382         q = strstr(p, needle);
00383     }
00384 
00385     /* no occurences of needle found */
00386     if(!list) {
00387         return strdup(str);
00388     }
00389     /* size of new string = size of old string + "number of occurences of needle"
00390      * x "size difference between replace and needle" */
00391     newsz = strlen(str) + 1 +
00392         alpm_list_count(list) * (replacesz - needlesz);
00393     newstr = calloc(newsz, sizeof(char));
00394     if(!newstr) {
00395         return NULL;
00396     }
00397 
00398     p = str;
00399     newp = newstr;
00400     for(i = list; i; i = alpm_list_next(i)) {
00401         q = i->data;
00402         if(q > p) {
00403             /* add chars between this occurence and last occurence, if any */
00404             memcpy(newp, p, (size_t)(q - p));
00405             newp += q - p;
00406         }
00407         memcpy(newp, replace, replacesz);
00408         newp += replacesz;
00409         p = q + needlesz;
00410     }
00411     alpm_list_free(list);
00412 
00413     if(*p) {
00414         /* add the rest of 'p' */
00415         strcpy(newp, p);
00416     }
00417 
00418     return newstr;
00419 }
00420 
00421 /** Splits a string into a list of strings using the chosen character as
00422  * a delimiter.
00423  *
00424  * @param str the string to split
00425  * @param splitchar the character to split at
00426  *
00427  * @return a list containing the duplicated strings
00428  */
00429 alpm_list_t *strsplit(const char *str, const char splitchar)
00430 {
00431     alpm_list_t *list = NULL;
00432     const char *prev = str;
00433     char *dup = NULL;
00434 
00435     while((str = strchr(str, splitchar))) {
00436         dup = strndup(prev, (size_t)(str - prev));
00437         if(dup == NULL) {
00438             return NULL;
00439         }
00440         list = alpm_list_add(list, dup);
00441 
00442         str++;
00443         prev = str;
00444     }
00445 
00446     dup = strdup(prev);
00447     if(dup == NULL) {
00448         return NULL;
00449     }
00450     list = alpm_list_add(list, dup);
00451 
00452     return list;
00453 }
00454 
00455 static size_t string_length(const char *s)
00456 {
00457     int len;
00458     wchar_t *wcstr;
00459 
00460     if(!s || s[0] == '\0') {
00461         return 0;
00462     }
00463     /* len goes from # bytes -> # chars -> # cols */
00464     len = strlen(s) + 1;
00465     wcstr = calloc(len, sizeof(wchar_t));
00466     len = mbstowcs(wcstr, s, len);
00467     len = wcswidth(wcstr, len);
00468     free(wcstr);
00469 
00470     return len;
00471 }
00472 
00473 void string_display(const char *title, const char *string)
00474 {
00475     if(title) {
00476         printf("%s ", title);
00477     }
00478     if(string == NULL || string[0] == '\0') {
00479         printf(_("None"));
00480     } else {
00481         /* compute the length of title + a space */
00482         size_t len = string_length(title) + 1;
00483         indentprint(string, len);
00484     }
00485     printf("\n");
00486 }
00487 
00488 static void table_print_line(const alpm_list_t *line, short col_padding,
00489         size_t colcount, size_t *widths, int *has_data)
00490 {
00491     size_t i, lastcol = 0;
00492     int need_padding = 0;
00493     const alpm_list_t *curcell;
00494 
00495     for(i = colcount; i > 0; i--) {
00496         if(has_data[i - 1]) {
00497             lastcol = i - 1;
00498             break;
00499         }
00500     }
00501 
00502     for(i = 0, curcell = line; curcell && i < colcount;
00503             i++, curcell = alpm_list_next(curcell)) {
00504         const char *value;
00505         int cell_padding;
00506 
00507         if(!has_data[i]) {
00508             continue;
00509         }
00510 
00511         value = curcell->data;
00512         /* silly printf requires padding size to be an int */
00513         cell_padding = (int)widths[i] - (int)string_length(value);
00514         if(cell_padding < 0) {
00515             cell_padding = 0;
00516         }
00517         if(need_padding) {
00518             printf("%*s", col_padding, "");
00519         }
00520         /* left-align all but the last column */
00521         if(i != lastcol) {
00522             printf("%s%*s", value, cell_padding, "");
00523         } else {
00524             printf("%*s%s", cell_padding, "", value);
00525         }
00526         need_padding = 1;
00527     }
00528 
00529     printf("\n");
00530 }
00531 
00532 
00533 
00534 /**
00535  * Find the max string width of each column. Also determines whether values
00536  * exist in the column and sets the value in has_data accordingly.
00537  * @param header a list of header strings
00538  * @param rows a list of lists of rows as strings
00539  * @param padding the amount of padding between columns
00540  * @param totalcols the total number of columns in the header and each row
00541  * @param widths a pointer to store width data
00542  * @param has_data a pointer to store whether column has data
00543  *
00544  * @return the total width of the table; 0 on failure
00545  */
00546 static size_t table_calc_widths(const alpm_list_t *header,
00547         const alpm_list_t *rows, short padding, size_t totalcols,
00548         size_t **widths, int **has_data)
00549 {
00550     const alpm_list_t *i;
00551     size_t curcol, totalwidth = 0, usefulcols = 0;
00552     size_t *colwidths;
00553     int *coldata;
00554 
00555     if(totalcols <= 0) {
00556         return 0;
00557     }
00558 
00559     colwidths = malloc(totalcols * sizeof(size_t));
00560     coldata = calloc(totalcols, sizeof(int));
00561     if(!colwidths || !coldata) {
00562         return 0;
00563     }
00564     /* header determines column count and initial values of longest_strs */
00565     for(i = header, curcol = 0; i; i = alpm_list_next(i), curcol++) {
00566         colwidths[curcol] = string_length(i->data);
00567         /* note: header does not determine whether column has data */
00568     }
00569 
00570     /* now find the longest string in each column */
00571     for(i = rows; i; i = alpm_list_next(i)) {
00572         /* grab first column of each row and iterate through columns */
00573         const alpm_list_t *j = i->data;
00574         for(curcol = 0; j; j = alpm_list_next(j), curcol++) {
00575             const char *str = j->data;
00576             size_t str_len = string_length(str);
00577 
00578             if(str_len > colwidths[curcol]) {
00579                 colwidths[curcol] = str_len;
00580             }
00581             if(str_len > 0) {
00582                 coldata[curcol] = 1;
00583             }
00584         }
00585     }
00586 
00587     for(i = header, curcol = 0; i; i = alpm_list_next(i), curcol++) {
00588         /* only include columns that have data */
00589         if(coldata[curcol]) {
00590             usefulcols++;
00591             totalwidth += colwidths[curcol];
00592         }
00593     }
00594 
00595     /* add padding between columns */
00596     if(usefulcols > 0) {
00597         totalwidth += padding * (usefulcols - 1);
00598     }
00599 
00600     *widths = colwidths;
00601     *has_data = coldata;
00602     return totalwidth;
00603 }
00604 
00605 /** Displays the list in table format
00606  *
00607  * @param title the tables title
00608  * @param header the column headers. column count is determined by the nr
00609  *               of headers
00610  * @param rows the rows to display as a list of lists of strings. the outer
00611  *             list represents the rows, the inner list the cells (= columns)
00612  *
00613  * @return -1 if not enough terminal cols available, else 0
00614  */
00615 int table_display(const char *title, const alpm_list_t *header,
00616         const alpm_list_t *rows)
00617 {
00618     const unsigned short padding = 2;
00619     const alpm_list_t *i;
00620     size_t *widths = NULL, totalcols, totalwidth;
00621     int *has_data = NULL;
00622 
00623     if(rows == NULL || header == NULL) {
00624         return 0;
00625     }
00626 
00627     totalcols = alpm_list_count(header);
00628     totalwidth = table_calc_widths(header, rows, padding, totalcols,
00629             &widths, &has_data);
00630     /* return -1 if terminal is not wide enough */
00631     if(totalwidth > getcols()) {
00632         pm_printf(ALPM_LOG_WARNING,
00633                 _("insufficient columns available for table display\n"));
00634         return -1;
00635     }
00636     if(!totalwidth || !widths || !has_data) {
00637         return -1;
00638     }
00639 
00640     if(title != NULL) {
00641         printf("%s\n\n", title);
00642     }
00643 
00644     table_print_line(header, padding, totalcols, widths, has_data);
00645     printf("\n");
00646 
00647     for(i = rows; i; i = alpm_list_next(i)) {
00648         table_print_line(i->data, padding, totalcols, widths, has_data);
00649     }
00650 
00651     free(widths);
00652     free(has_data);
00653     return 0;
00654 }
00655 
00656 void list_display(const char *title, const alpm_list_t *list)
00657 {
00658     const alpm_list_t *i;
00659     size_t len = 0;
00660 
00661     if(title) {
00662         len = string_length(title) + 1;
00663         printf("%s ", title);
00664     }
00665 
00666     if(!list) {
00667         printf("%s\n", _("None"));
00668     } else {
00669         const unsigned short maxcols = getcols();
00670         size_t cols = len;
00671         const char *str = list->data;
00672         fputs(str, stdout);
00673         cols += string_length(str);
00674         for(i = alpm_list_next(list); i; i = alpm_list_next(i)) {
00675             str = i->data;
00676             size_t s = string_length(str);
00677             /* wrap only if we have enough usable column space */
00678             if(maxcols > len && cols + s + 2 >= maxcols) {
00679                 size_t j;
00680                 cols = len;
00681                 printf("\n");
00682                 for(j = 1; j <= len; j++) {
00683                     printf(" ");
00684                 }
00685             } else if(cols != len) {
00686                 /* 2 spaces are added if this is not the first element on a line. */
00687                 printf("  ");
00688                 cols += 2;
00689             }
00690             fputs(str, stdout);
00691             cols += s;
00692         }
00693         putchar('\n');
00694     }
00695 }
00696 
00697 void list_display_linebreak(const char *title, const alpm_list_t *list)
00698 {
00699     size_t len = 0;
00700 
00701     if(title) {
00702         len = string_length(title) + 1;
00703         printf("%s ", title);
00704     }
00705 
00706     if(!list) {
00707         printf("%s\n", _("None"));
00708     } else {
00709         const alpm_list_t *i;
00710         /* Print the first element */
00711         indentprint((const char *)list->data, len);
00712         printf("\n");
00713         /* Print the rest */
00714         for(i = alpm_list_next(list); i; i = alpm_list_next(i)) {
00715             size_t j;
00716             for(j = 1; j <= len; j++) {
00717                 printf(" ");
00718             }
00719             indentprint((const char *)i->data, len);
00720             printf("\n");
00721         }
00722     }
00723 }
00724 
00725 void signature_display(const char *title, alpm_siglist_t *siglist)
00726 {
00727     size_t len = 0;
00728 
00729     if(title) {
00730         len = string_length(title) + 1;
00731         printf("%s ", title);
00732     }
00733     if(siglist->count == 0) {
00734         printf(_("None"));
00735     } else {
00736         size_t i;
00737         for(i = 0; i < siglist->count; i++) {
00738             char *sigline;
00739             const char *status, *validity, *name;
00740             int ret;
00741             alpm_sigresult_t *result = siglist->results + i;
00742             /* Don't re-indent the first result */
00743             if(i != 0) {
00744                 size_t j;
00745                 for(j = 1; j <= len; j++) {
00746                     printf(" ");
00747                 }
00748             }
00749             switch(result->status) {
00750                 case ALPM_SIGSTATUS_VALID:
00751                     status = _("Valid");
00752                     break;
00753                 case ALPM_SIGSTATUS_KEY_EXPIRED:
00754                     status = _("Key expired");
00755                     break;
00756                 case ALPM_SIGSTATUS_SIG_EXPIRED:
00757                     status = _("Expired");
00758                     break;
00759                 case ALPM_SIGSTATUS_INVALID:
00760                     status = _("Invalid");
00761                     break;
00762                 case ALPM_SIGSTATUS_KEY_UNKNOWN:
00763                     status = _("Key unknown");
00764                     break;
00765                 case ALPM_SIGSTATUS_KEY_DISABLED:
00766                     status = _("Key disabled");
00767                     break;
00768                 default:
00769                     status = _("Signature error");
00770                     break;
00771             }
00772             switch(result->validity) {
00773                 case ALPM_SIGVALIDITY_FULL:
00774                     validity = _("full trust");
00775                     break;
00776                 case ALPM_SIGVALIDITY_MARGINAL:
00777                     validity = _("marginal trust");
00778                     break;
00779                 case ALPM_SIGVALIDITY_NEVER:
00780                     validity = _("never trust");
00781                     break;
00782                 case ALPM_SIGVALIDITY_UNKNOWN:
00783                 default:
00784                     validity = _("unknown trust");
00785                     break;
00786             }
00787             name = result->key.uid ? result->key.uid : result->key.fingerprint;
00788             ret = pm_asprintf(&sigline, _("%s, %s from \"%s\""),
00789                     status, validity, name);
00790             if(ret == -1) {
00791                 pm_printf(ALPM_LOG_ERROR,  _("failed to allocate string\n"));
00792                 continue;
00793             }
00794             indentprint(sigline, len);
00795             printf("\n");
00796             free(sigline);
00797         }
00798     }
00799 }
00800 
00801 /* creates a header row for use with table_display */
00802 static alpm_list_t *create_verbose_header(int dl_size)
00803 {
00804     alpm_list_t *res = NULL;
00805     char *str;
00806 
00807     str = _("Name");
00808     res = alpm_list_add(res, str);
00809     str = _("Old Version");
00810     res = alpm_list_add(res, str);
00811     str = _("New Version");
00812     res = alpm_list_add(res, str);
00813     str = _("Net Change");
00814     res = alpm_list_add(res, str);
00815     if(dl_size) {
00816         str = _("Download Size");
00817         res = alpm_list_add(res, str);
00818     }
00819 
00820     return res;
00821 }
00822 
00823 /* returns package info as list of strings */
00824 static alpm_list_t *create_verbose_row(pm_target_t *target, int dl_size)
00825 {
00826     char *str;
00827     off_t size = 0;
00828     double human_size;
00829     const char *label;
00830     alpm_list_t *ret = NULL;
00831 
00832     /* a row consists of the package name, */
00833     if(target->install) {
00834         const alpm_db_t *db = alpm_pkg_get_db(target->install);
00835         if(db) {
00836             pm_asprintf(&str, "%s/%s", alpm_db_get_name(db), alpm_pkg_get_name(target->install));
00837         } else {
00838             pm_asprintf(&str, "%s", alpm_pkg_get_name(target->install));
00839         }
00840     } else {
00841         pm_asprintf(&str, "%s", alpm_pkg_get_name(target->remove));
00842     }
00843     ret = alpm_list_add(ret, str);
00844 
00845     /* old and new versions */
00846     pm_asprintf(&str, "%s",
00847             target->remove != NULL ? alpm_pkg_get_version(target->remove) : "");
00848     ret = alpm_list_add(ret, str);
00849 
00850     pm_asprintf(&str, "%s",
00851             target->install != NULL ? alpm_pkg_get_version(target->install) : "");
00852     ret = alpm_list_add(ret, str);
00853 
00854     /* and size */
00855     size -= target->remove ? alpm_pkg_get_isize(target->remove) : 0;
00856     size += target->install ? alpm_pkg_get_isize(target->install) : 0;
00857     human_size = humanize_size(size, 'M', &label);
00858     pm_asprintf(&str, "%.2f %s", human_size, label);
00859     ret = alpm_list_add(ret, str);
00860 
00861     if(dl_size) {
00862         size = target->install ? alpm_pkg_download_size(target->install) : 0;
00863         human_size = humanize_size(size, 'M', &label);
00864         if(size != 0) {
00865             pm_asprintf(&str, "%.2f %s", human_size, label);
00866         } else {
00867             str = strdup("");
00868         }
00869         ret = alpm_list_add(ret, str);
00870     }
00871 
00872     return ret;
00873 }
00874 
00875 /* prepare a list of pkgs to display */
00876 static void _display_targets(alpm_list_t *targets, int verbose)
00877 {
00878     char *str;
00879     const char *label;
00880     double size;
00881     off_t isize = 0, rsize = 0, dlsize = 0;
00882     alpm_list_t *i, *rows = NULL, *names = NULL;
00883     int show_dl_size = config->op == PM_OP_SYNC;
00884 
00885     if(!targets) {
00886         return;
00887     }
00888 
00889     /* gather package info */
00890     for(i = targets; i; i = alpm_list_next(i)) {
00891         pm_target_t *target = i->data;
00892 
00893         if(target->install) {
00894             dlsize += alpm_pkg_download_size(target->install);
00895             isize += alpm_pkg_get_isize(target->install);
00896         }
00897         if(target->remove) {
00898             /* add up size of all removed packages */
00899             rsize += alpm_pkg_get_isize(target->remove);
00900         }
00901 
00902         /* form data for both verbose and non-verbose display */
00903         rows = alpm_list_add(rows, create_verbose_row(target, show_dl_size));
00904         if(target->install) {
00905             pm_asprintf(&str, "%s-%s", alpm_pkg_get_name(target->install),
00906                     alpm_pkg_get_version(target->install));
00907         } else if(isize == 0) {
00908             pm_asprintf(&str, "%s-%s", alpm_pkg_get_name(target->remove),
00909                     alpm_pkg_get_version(target->remove));
00910         } else {
00911             pm_asprintf(&str, "%s-%s [removal]", alpm_pkg_get_name(target->remove),
00912                     alpm_pkg_get_version(target->remove));
00913         }
00914         names = alpm_list_add(names, str);
00915     }
00916 
00917     /* print to screen */
00918     pm_asprintf(&str, _("Targets (%d):"), alpm_list_count(targets));
00919 
00920     printf("\n");
00921     if(verbose) {
00922         alpm_list_t *header = create_verbose_header(show_dl_size);
00923         if(table_display(str, header, rows) != 0) {
00924             /* fallback to list display if table wouldn't fit */
00925             list_display(str, names);
00926         }
00927         alpm_list_free(header);
00928     } else {
00929         list_display(str, names);
00930     }
00931     printf("\n");
00932 
00933     /* rows is a list of lists of strings, free inner lists here */
00934     for(i = rows; i; i = alpm_list_next(i)) {
00935         alpm_list_t *lp = i->data;
00936         FREELIST(lp);
00937     }
00938     alpm_list_free(rows);
00939     FREELIST(names);
00940     free(str);
00941 
00942     if(dlsize > 0 || config->op_s_downloadonly) {
00943         size = humanize_size(dlsize, 'M', &label);
00944         printf(_("Total Download Size:    %.2f %s\n"), size, label);
00945     }
00946     if(!config->op_s_downloadonly) {
00947         if(isize > 0) {
00948             size = humanize_size(isize, 'M', &label);
00949             printf(_("Total Installed Size:   %.2f %s\n"), size, label);
00950         }
00951         if(rsize > 0 && isize == 0) {
00952             size = humanize_size(rsize, 'M', &label);
00953             printf(_("Total Removed Size:     %.2f %s\n"), size, label);
00954         }
00955         /* only show this net value if different from raw installed size */
00956         if(isize > 0 && rsize > 0) {
00957             size = humanize_size(isize - rsize, 'M', &label);
00958             printf(_("Net Upgrade Size:       %.2f %s\n"), size, label);
00959         }
00960     }
00961 }
00962 
00963 static int target_cmp(const void *p1, const void *p2)
00964 {
00965     const pm_target_t *targ1 = p1;
00966     const pm_target_t *targ2 = p2;
00967     /* explicit are always sorted after implicit (e.g. deps, pulled targets) */
00968     if(targ1->is_explicit != targ2->is_explicit) {
00969         return targ1->is_explicit > targ2->is_explicit;
00970     }
00971     const char *name1 = targ1->install ?
00972         alpm_pkg_get_name(targ1->install) : alpm_pkg_get_name(targ1->remove);
00973     const char *name2 = targ2->install ?
00974         alpm_pkg_get_name(targ2->install) : alpm_pkg_get_name(targ2->remove);
00975     return strcmp(name1, name2);
00976 }
00977 
00978 static int pkg_cmp(const void *p1, const void *p2)
00979 {
00980     /* explicit cast due to (un)necessary removal of const */
00981     alpm_pkg_t *pkg1 = (alpm_pkg_t *)p1;
00982     alpm_pkg_t *pkg2 = (alpm_pkg_t *)p2;
00983     return strcmp(alpm_pkg_get_name(pkg1), alpm_pkg_get_name(pkg2));
00984 }
00985 
00986 void display_targets(void)
00987 {
00988     alpm_list_t *i, *targets = NULL;
00989     alpm_db_t *db_local = alpm_option_get_localdb(config->handle);
00990 
00991     for(i = alpm_trans_get_add(config->handle); i; i = alpm_list_next(i)) {
00992         alpm_pkg_t *pkg = i->data;
00993         pm_target_t *targ = calloc(1, sizeof(pm_target_t));
00994         if(!targ) return;
00995         targ->install = pkg;
00996         targ->remove = alpm_db_get_pkg(db_local, alpm_pkg_get_name(pkg));
00997         if(alpm_list_find(config->explicit_adds, pkg, pkg_cmp)) {
00998             targ->is_explicit = 1;
00999         }
01000         targets = alpm_list_add(targets, targ);
01001     }
01002     for(i = alpm_trans_get_remove(config->handle); i; i = alpm_list_next(i)) {
01003         alpm_pkg_t *pkg = i->data;
01004         pm_target_t *targ = calloc(1, sizeof(pm_target_t));
01005         if(!targ) return;
01006         targ->remove = pkg;
01007         if(alpm_list_find(config->explicit_removes, pkg, pkg_cmp)) {
01008             targ->is_explicit = 1;
01009         }
01010         targets = alpm_list_add(targets, targ);
01011     }
01012 
01013     targets = alpm_list_msort(targets, alpm_list_count(targets), target_cmp);
01014     _display_targets(targets, config->verbosepkglists);
01015     FREELIST(targets);
01016 }
01017 
01018 static off_t pkg_get_size(alpm_pkg_t *pkg)
01019 {
01020     switch(config->op) {
01021         case PM_OP_SYNC:
01022             return alpm_pkg_download_size(pkg);
01023         case PM_OP_UPGRADE:
01024             return alpm_pkg_get_size(pkg);
01025         default:
01026             return alpm_pkg_get_isize(pkg);
01027     }
01028 }
01029 
01030 static char *pkg_get_location(alpm_pkg_t *pkg)
01031 {
01032     alpm_list_t *servers;
01033     char *string = NULL;
01034     switch(config->op) {
01035         case PM_OP_SYNC:
01036             servers = alpm_db_get_servers(alpm_pkg_get_db(pkg));
01037             if(servers) {
01038                 pm_asprintf(&string, "%s/%s", servers->data,
01039                         alpm_pkg_get_filename(pkg));
01040                 return string;
01041             }
01042         case PM_OP_UPGRADE:
01043             return strdup(alpm_pkg_get_filename(pkg));
01044         default:
01045             pm_asprintf(&string, "%s-%s", alpm_pkg_get_name(pkg), alpm_pkg_get_version(pkg));
01046             return string;
01047     }
01048 }
01049 
01050 /** Converts sizes in bytes into human readable units.
01051  *
01052  * @param bytes the size in bytes
01053  * @param target_unit '\0' or a short label. If equal to one of the short unit
01054  * labels ('B', 'K', ...) bytes is converted to target_unit; if '\0', the first
01055  * unit which will bring the value to below a threshold of 2048 will be chosen.
01056  * @param long_labels whether to use short ("K") or long ("KiB") unit labels
01057  * @param label will be set to the appropriate unit label
01058  *
01059  * @return the size in the appropriate unit
01060  */
01061 double humanize_size(off_t bytes, const char target_unit, const char **label)
01062 {
01063     static const char *labels[] = {"B", "KiB", "MiB", "GiB",
01064         "TiB", "PiB", "EiB", "ZiB", "YiB"};
01065     static const int unitcount = sizeof(labels) / sizeof(labels[0]);
01066 
01067     double val = (double)bytes;
01068     int index;
01069 
01070     for(index = 0; index < unitcount - 1; index++) {
01071         if(target_unit != '\0' && labels[index][0] == target_unit) {
01072             break;
01073         } else if(target_unit == '\0' && val <= 2048.0 && val >= -2048.0) {
01074             break;
01075         }
01076         val /= 1024.0;
01077     }
01078 
01079     if(label) {
01080         *label = labels[index];
01081     }
01082 
01083     return val;
01084 }
01085 
01086 void print_packages(const alpm_list_t *packages)
01087 {
01088     const alpm_list_t *i;
01089     if(!config->print_format) {
01090         config->print_format = strdup("%l");
01091     }
01092     for(i = packages; i; i = alpm_list_next(i)) {
01093         alpm_pkg_t *pkg = i->data;
01094         char *string = strdup(config->print_format);
01095         char *temp = string;
01096         /* %n : pkgname */
01097         if(strstr(temp, "%n")) {
01098             string = strreplace(temp, "%n", alpm_pkg_get_name(pkg));
01099             free(temp);
01100             temp = string;
01101         }
01102         /* %v : pkgver */
01103         if(strstr(temp, "%v")) {
01104             string = strreplace(temp, "%v", alpm_pkg_get_version(pkg));
01105             free(temp);
01106             temp = string;
01107         }
01108         /* %l : location */
01109         if(strstr(temp, "%l")) {
01110             char *pkgloc = pkg_get_location(pkg);
01111             string = strreplace(temp, "%l", pkgloc);
01112             free(pkgloc);
01113             free(temp);
01114             temp = string;
01115         }
01116         /* %r : repo */
01117         if(strstr(temp, "%r")) {
01118             const char *repo = "local";
01119             alpm_db_t *db = alpm_pkg_get_db(pkg);
01120             if(db) {
01121                 repo = alpm_db_get_name(db);
01122             }
01123             string = strreplace(temp, "%r", repo);
01124             free(temp);
01125             temp = string;
01126         }
01127         /* %s : size */
01128         if(strstr(temp, "%s")) {
01129             char *size;
01130             pm_asprintf(&size, "%jd", (intmax_t)pkg_get_size(pkg));
01131             string = strreplace(temp, "%s", size);
01132             free(size);
01133             free(temp);
01134         }
01135         printf("%s\n",string);
01136         free(string);
01137     }
01138 }
01139 
01140 /* Helper function for comparing strings using the
01141  * alpm "compare func" signature */
01142 int str_cmp(const void *s1, const void *s2)
01143 {
01144     return strcmp(s1, s2);
01145 }
01146 
01147 void display_new_optdepends(alpm_pkg_t *oldpkg, alpm_pkg_t *newpkg)
01148 {
01149     alpm_list_t *old = alpm_pkg_get_optdepends(oldpkg);
01150     alpm_list_t *new = alpm_pkg_get_optdepends(newpkg);
01151     alpm_list_t *optdeps = alpm_list_diff(new,old,str_cmp);
01152     if(optdeps) {
01153         printf(_("New optional dependencies for %s\n"), alpm_pkg_get_name(newpkg));
01154         list_display_linebreak("   ", optdeps);
01155     }
01156     alpm_list_free(optdeps);
01157 }
01158 
01159 void display_optdepends(alpm_pkg_t *pkg)
01160 {
01161     alpm_list_t *optdeps = alpm_pkg_get_optdepends(pkg);
01162     if(optdeps) {
01163         printf(_("Optional dependencies for %s\n"), alpm_pkg_get_name(pkg));
01164         list_display_linebreak("   ", optdeps);
01165     }
01166 }
01167 
01168 static void display_repo_list(const char *dbname, alpm_list_t *list)
01169 {
01170     const char *prefix= "  ";
01171 
01172     printf(":: ");
01173     printf(_("Repository %s\n"), dbname);
01174     list_display(prefix, list);
01175 }
01176 
01177 void select_display(const alpm_list_t *pkglist)
01178 {
01179     const alpm_list_t *i;
01180     int nth = 1;
01181     alpm_list_t *list = NULL;
01182     char *string = NULL;
01183     const char *dbname = NULL;
01184 
01185     for(i = pkglist; i; i = i->next) {
01186         alpm_pkg_t *pkg = i->data;
01187         alpm_db_t *db = alpm_pkg_get_db(pkg);
01188 
01189         if(!dbname)
01190             dbname = alpm_db_get_name(db);
01191         if(strcmp(alpm_db_get_name(db), dbname) != 0) {
01192             display_repo_list(dbname, list);
01193             FREELIST(list);
01194             dbname = alpm_db_get_name(db);
01195         }
01196         string = NULL;
01197         pm_asprintf(&string, "%d) %s", nth, alpm_pkg_get_name(pkg));
01198         list = alpm_list_add(list, string);
01199         nth++;
01200     }
01201     display_repo_list(dbname, list);
01202     FREELIST(list);
01203 }
01204 
01205 static int parseindex(char *s, int *val, int min, int max)
01206 {
01207     char *endptr = NULL;
01208     int n = strtol(s, &endptr, 10);
01209     if(*endptr == '\0') {
01210         if(n < min || n > max) {
01211             pm_printf(ALPM_LOG_ERROR,
01212                     _("invalid value: %d is not between %d and %d\n"),
01213                     n, min, max);
01214             return -1;
01215         }
01216         *val = n;
01217         return 0;
01218     } else {
01219         pm_printf(ALPM_LOG_ERROR, _("invalid number: %s\n"), s);
01220         return -1;
01221     }
01222 }
01223 
01224 static int multiselect_parse(char *array, int count, char *response)
01225 {
01226     char *str, *saveptr;
01227 
01228     for(str = response; ; str = NULL) {
01229         int include = 1;
01230         int start, end;
01231         size_t len;
01232         char *ends = NULL;
01233         char *starts = strtok_r(str, " ", &saveptr);
01234 
01235         if(starts == NULL) {
01236             break;
01237         }
01238         len = strtrim(starts);
01239         if(len == 0)
01240             continue;
01241 
01242         if(*starts == '^') {
01243             starts++;
01244             len--;
01245             include = 0;
01246         } else if(str) {
01247             /* if first token is including, we unselect all targets */
01248             memset(array, 0, count);
01249         }
01250 
01251         if(len > 1) {
01252             /* check for range */
01253             char *p;
01254             if((p = strchr(starts + 1, '-'))) {
01255                 *p = 0;
01256                 ends = p + 1;
01257             }
01258         }
01259 
01260         if(parseindex(starts, &start, 1, count) != 0)
01261             return -1;
01262 
01263         if(!ends) {
01264             array[start-1] = include;
01265         } else {
01266             int d;
01267             if(parseindex(ends, &end, start, count) != 0) {
01268                 return -1;
01269             }
01270             for(d = start; d <= end; d++) {
01271                 array[d-1] = include;
01272             }
01273         }
01274     }
01275 
01276     return 0;
01277 }
01278 
01279 int multiselect_question(char *array, int count)
01280 {
01281     char *response, *lastchar;
01282     FILE *stream;
01283     size_t response_len = 64;
01284 
01285     if(config->noconfirm) {
01286         stream = stdout;
01287     } else {
01288         /* Use stderr so questions are always displayed when redirecting output */
01289         stream = stderr;
01290     }
01291 
01292     response = malloc(response_len);
01293     if(!response) {
01294         return -1;
01295     }
01296     lastchar = response + response_len - 1;
01297     /* sentinel byte to later see if we filled up the entire string */
01298     *lastchar = 1;
01299 
01300     while(1) {
01301         memset(array, 1, count);
01302 
01303         fprintf(stream, "\n");
01304         fprintf(stream, _("Enter a selection (default=all)"));
01305         fprintf(stream, ": ");
01306         fflush(stream);
01307 
01308         if(config->noconfirm) {
01309             fprintf(stream, "\n");
01310             break;
01311         }
01312 
01313         flush_term_input();
01314 
01315         if(fgets(response, response_len, stdin)) {
01316             const size_t response_incr = 64;
01317             size_t len;
01318             /* handle buffer not being large enough to read full line case */
01319             while(*lastchar == '\0' && lastchar[-1] != '\n') {
01320                 response_len += response_incr;
01321                 response = realloc(response, response_len);
01322                 if(!response) {
01323                     return -1;
01324                 }
01325                 lastchar = response + response_len - 1;
01326                 /* sentinel byte */
01327                 *lastchar = 1;
01328                 if(fgets(response + response_len - response_incr - 1,
01329                             response_incr + 1, stdin) == 0) {
01330                     free(response);
01331                     return -1;
01332                 }
01333             }
01334 
01335             len = strtrim(response);
01336             if(len > 0) {
01337                 if(multiselect_parse(array, count, response) == -1) {
01338                     /* only loop if user gave an invalid answer */
01339                     continue;
01340                 }
01341             }
01342             break;
01343         } else {
01344             free(response);
01345             return -1;
01346         }
01347     }
01348 
01349     free(response);
01350     return 0;
01351 }
01352 
01353 int select_question(int count)
01354 {
01355     char response[32];
01356     FILE *stream;
01357     int preset = 1;
01358 
01359     if(config->noconfirm) {
01360         stream = stdout;
01361     } else {
01362         /* Use stderr so questions are always displayed when redirecting output */
01363         stream = stderr;
01364     }
01365 
01366     while(1) {
01367         fprintf(stream, "\n");
01368         fprintf(stream, _("Enter a number (default=%d)"), preset);
01369         fprintf(stream, ": ");
01370 
01371         if(config->noconfirm) {
01372             fprintf(stream, "\n");
01373             break;
01374         }
01375 
01376         flush_term_input();
01377 
01378         if(fgets(response, sizeof(response), stdin)) {
01379             size_t len = strtrim(response);
01380             if(len > 0) {
01381                 int n;
01382                 if(parseindex(response, &n, 1, count) != 0)
01383                     continue;
01384                 return (n - 1);
01385             }
01386         }
01387         break;
01388     }
01389 
01390     return (preset - 1);
01391 }
01392 
01393 
01394 /* presents a prompt and gets a Y/N answer */
01395 static int question(short preset, char *fmt, va_list args)
01396 {
01397     char response[32];
01398     FILE *stream;
01399 
01400     if(config->noconfirm) {
01401         stream = stdout;
01402     } else {
01403         /* Use stderr so questions are always displayed when redirecting output */
01404         stream = stderr;
01405     }
01406 
01407     /* ensure all text makes it to the screen before we prompt the user */
01408     fflush(stdout);
01409     fflush(stderr);
01410 
01411     vfprintf(stream, fmt, args);
01412 
01413     if(preset) {
01414         fprintf(stream, " %s ", _("[Y/n]"));
01415     } else {
01416         fprintf(stream, " %s ", _("[y/N]"));
01417     }
01418 
01419     if(config->noconfirm) {
01420         fprintf(stream, "\n");
01421         return preset;
01422     }
01423 
01424     fflush(stream);
01425     flush_term_input();
01426 
01427     if(fgets(response, sizeof(response), stdin)) {
01428         size_t len = strtrim(response);
01429         if(len == 0) {
01430             return preset;
01431         }
01432 
01433         if(strcasecmp(response, _("Y")) == 0 || strcasecmp(response, _("YES")) == 0) {
01434             return 1;
01435         } else if(strcasecmp(response, _("N")) == 0 || strcasecmp(response, _("NO")) == 0) {
01436             return 0;
01437         }
01438     }
01439     return 0;
01440 }
01441 
01442 int yesno(char *fmt, ...)
01443 {
01444     int ret;
01445     va_list args;
01446 
01447     va_start(args, fmt);
01448     ret = question(1, fmt, args);
01449     va_end(args);
01450 
01451     return ret;
01452 }
01453 
01454 int noyes(char *fmt, ...)
01455 {
01456     int ret;
01457     va_list args;
01458 
01459     va_start(args, fmt);
01460     ret = question(0, fmt, args);
01461     va_end(args);
01462 
01463     return ret;
01464 }
01465 
01466 int pm_printf(alpm_loglevel_t level, const char *format, ...)
01467 {
01468     int ret;
01469     va_list args;
01470 
01471     /* print the message using va_arg list */
01472     va_start(args, format);
01473     ret = pm_vfprintf(stderr, level, format, args);
01474     va_end(args);
01475 
01476     return ret;
01477 }
01478 
01479 int pm_asprintf(char **string, const char *format, ...)
01480 {
01481     int ret = 0;
01482     va_list args;
01483 
01484     /* print the message using va_arg list */
01485     va_start(args, format);
01486     if(vasprintf(string, format, args) == -1) {
01487         pm_printf(ALPM_LOG_ERROR,  _("failed to allocate string\n"));
01488         ret = -1;
01489     }
01490     va_end(args);
01491 
01492     return ret;
01493 }
01494 
01495 int pm_vasprintf(char **string, alpm_loglevel_t level, const char *format, va_list args)
01496 {
01497     int ret = 0;
01498     char *msg = NULL;
01499 
01500     /* if current logmask does not overlap with level, do not print msg */
01501     if(!(config->logmask & level)) {
01502         return ret;
01503     }
01504 
01505     /* print the message using va_arg list */
01506     ret = vasprintf(&msg, format, args);
01507 
01508     /* print a prefix to the message */
01509     switch(level) {
01510         case ALPM_LOG_ERROR:
01511             pm_asprintf(string, _("error: %s"), msg);
01512             break;
01513         case ALPM_LOG_WARNING:
01514             pm_asprintf(string, _("warning: %s"), msg);
01515             break;
01516         case ALPM_LOG_DEBUG:
01517             pm_asprintf(string, "debug: %s", msg);
01518             break;
01519         case ALPM_LOG_FUNCTION:
01520             pm_asprintf(string, "function: %s", msg);
01521             break;
01522         default:
01523             pm_asprintf(string, "%s", msg);
01524             break;
01525     }
01526     free(msg);
01527 
01528     return ret;
01529 }
01530 
01531 int pm_vfprintf(FILE *stream, alpm_loglevel_t level, const char *format, va_list args)
01532 {
01533     int ret = 0;
01534 
01535     /* if current logmask does not overlap with level, do not print msg */
01536     if(!(config->logmask & level)) {
01537         return ret;
01538     }
01539 
01540 #if defined(PACMAN_DEBUG)
01541     /* If debug is on, we'll timestamp the output */
01542     if(config->logmask & ALPM_LOG_DEBUG) {
01543         time_t t;
01544         struct tm *tmp;
01545         char timestr[10] = {0};
01546 
01547         t = time(NULL);
01548         tmp = localtime(&t);
01549         strftime(timestr, 9, "%H:%M:%S", tmp);
01550         timestr[8] = '\0';
01551 
01552         fprintf(stream, "[%s] ", timestr);
01553     }
01554 #endif
01555 
01556     /* print a prefix to the message */
01557     switch(level) {
01558         case ALPM_LOG_ERROR:
01559             fprintf(stream, _("error: "));
01560             break;
01561         case ALPM_LOG_WARNING:
01562             fprintf(stream, _("warning: "));
01563             break;
01564         case ALPM_LOG_DEBUG:
01565             fprintf(stream, "debug: ");
01566             break;
01567         case ALPM_LOG_FUNCTION:
01568             fprintf(stream, "function: ");
01569             break;
01570         default:
01571             break;
01572     }
01573 
01574     /* print the message using va_arg list */
01575     ret = vfprintf(stream, format, args);
01576     return ret;
01577 }
01578 
01579 #ifndef HAVE_STRNDUP
01580 /* A quick and dirty implementation derived from glibc */
01581 static size_t strnlen(const char *s, size_t max)
01582 {
01583     register const char *p;
01584     for(p = s; *p && max--; ++p);
01585     return (p - s);
01586 }
01587 
01588 char *strndup(const char *s, size_t n)
01589 {
01590   size_t len = strnlen(s, n);
01591   char *new = (char *) malloc(len + 1);
01592 
01593   if(new == NULL)
01594     return NULL;
01595 
01596   new[len] = '\0';
01597   return (char *)memcpy(new, s, len);
01598 }
01599 #endif
01600 
01601 /* vim: set ts=2 sw=2 noet: */