diff options
Diffstat (limited to 'src/pacman/util.c')
-rw-r--r-- | src/pacman/util.c | 424 |
1 files changed, 331 insertions, 93 deletions
diff --git a/src/pacman/util.c b/src/pacman/util.c index 3d268031..8631075f 100644 --- a/src/pacman/util.c +++ b/src/pacman/util.c @@ -31,12 +31,14 @@ #include <stdint.h> /* intmax_t */ #include <string.h> #include <errno.h> -#include <fcntl.h> #include <ctype.h> #include <dirent.h> #include <unistd.h> #include <limits.h> #include <wchar.h> +#ifdef HAVE_TERMIOS_H +#include <termios.h> /* tcflush */ +#endif #include <alpm.h> #include <alpm_list.h> @@ -68,9 +70,9 @@ int trans_init(pmtransflag_t flags) fprintf(stderr, _(" try running pacman-db-upgrade\n")); } - return(-1); + return -1; } - return(0); + return 0; } int trans_release(void) @@ -78,28 +80,40 @@ int trans_release(void) if(alpm_trans_release() == -1) { pm_fprintf(stderr, PM_LOG_ERROR, _("failed to release transaction (%s)\n"), alpm_strerrorlast()); - return(-1); + return -1; } - return(0); + return 0; } int needs_root(void) { switch(config->op) { case PM_OP_DATABASE: - return(1); + return 1; case PM_OP_UPGRADE: case PM_OP_REMOVE: - return(!config->print); + return !config->print; case PM_OP_SYNC: - return(config->op_s_clean || config->op_s_sync || + return (config->op_s_clean || config->op_s_sync || (!config->group && !config->op_s_info && !config->op_q_list && !config->op_s_search && !config->print)); default: - return(0); + return 0; } } +/* discard unhandled input on the terminal's input buffer */ +static int flush_term_input(void) { +#ifdef HAVE_TCFLUSH + if(isatty(fileno(stdin))) { + return(tcflush(fileno(stdin), TCIFLUSH)); + } +#endif + + /* fail silently */ + return 0; +} + /* gets the current screen column width */ int getcols(void) { @@ -125,24 +139,24 @@ int rmrf(const char *path) DIR *dirp; if(!unlink(path)) { - return(0); + return 0; } else { if(errno == ENOENT) { - return(0); + return 0; } else if(errno == EPERM) { /* fallthrough */ } else if(errno == EISDIR) { /* fallthrough */ } else if(errno == ENOTDIR) { - return(1); + return 1; } else { /* not a directory */ - return(1); + return 1; } dirp = opendir(path); if(!dirp) { - return(1); + return 1; } for(dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) { if(dp->d_ino) { @@ -157,7 +171,7 @@ int rmrf(const char *path) if(rmdir(path)) { errflag++; } - return(errflag); + return errflag; } } @@ -170,9 +184,9 @@ const char *mbasename(const char *path) { const char *last = strrchr(path, '/'); if(last) { - return(last + 1); + return last + 1; } - return(path); + return path; } /** Parse the dirname of a program from a path. @@ -187,7 +201,7 @@ char *mdirname(const char *path) /* null or empty path */ if(path == NULL || path == '\0') { - return(strdup(".")); + return strdup("."); } ret = strdup(path); @@ -196,11 +210,11 @@ char *mdirname(const char *path) if(last != NULL) { /* we found a '/', so terminate our string */ *last = '\0'; - return(ret); + return ret; } /* no slash found */ free(ret); - return(strdup(".")); + return strdup("."); } /* output a string, but wrap words properly with a specified indentation @@ -286,7 +300,7 @@ char *strtrim(char *str) if(str == NULL || *str == '\0') { /* string is empty, so we're done. */ - return(str); + return str; } while(isspace((unsigned char)*pch)) { @@ -298,7 +312,7 @@ char *strtrim(char *str) /* check if there wasn't anything but whitespace in the string. */ if(*str == '\0') { - return(str); + return str; } pch = (str + (strlen(str) - 1)); @@ -307,7 +321,7 @@ char *strtrim(char *str) } *++pch = '\0'; - return(str); + return str; } /* Replace all occurances of 'needle' with 'replace' in 'str', returning @@ -321,7 +335,7 @@ char *strreplace(const char *str, const char *needle, const char *replace) size_t newsz; if(!str) { - return(NULL); + return NULL; } p = str; @@ -334,7 +348,7 @@ char *strreplace(const char *str, const char *needle, const char *replace) /* no occurences of needle found */ if(!list) { - return(strdup(str)); + return strdup(str); } /* size of new string = size of old string + "number of occurences of needle" * x "size difference between replace and needle" */ @@ -342,7 +356,7 @@ char *strreplace(const char *str, const char *needle, const char *replace) alpm_list_count(list) * (replacesz - needlesz); newstr = malloc(newsz); if(!newstr) { - return(NULL); + return NULL; } *newstr = '\0'; @@ -368,7 +382,7 @@ char *strreplace(const char *str, const char *needle, const char *replace) } *newp = '\0'; - return(newstr); + return newstr; } /** Splits a string into a list of strings using the chosen character as @@ -388,7 +402,7 @@ alpm_list_t *strsplit(const char *str, const char splitchar) while((str = strchr(str, splitchar))) { dup = strndup(prev, (size_t)(str - prev)); if(dup == NULL) { - return(NULL); + return NULL; } list = alpm_list_add(list, dup); @@ -398,11 +412,11 @@ alpm_list_t *strsplit(const char *str, const char splitchar) dup = strdup(prev); if(dup == NULL) { - return(NULL); + return NULL; } list = alpm_list_add(list, dup); - return(list); + return list; } static int string_length(const char *s) @@ -411,7 +425,7 @@ static int string_length(const char *s) wchar_t *wcstr; if(!s) { - return(0); + return 0; } /* len goes from # bytes -> # chars -> # cols */ len = strlen(s) + 1; @@ -420,7 +434,7 @@ static int string_length(const char *s) len = wcswidth(wcstr, len); free(wcstr); - return(len); + return len; } void string_display(const char *title, const char *string) @@ -440,6 +454,117 @@ void string_display(const char *title, const char *string) printf("\n"); } +static void table_print_line(const alpm_list_t *line, + const alpm_list_t *formats) +{ + const alpm_list_t *curformat = formats; + const alpm_list_t *curcell = line; + + while(curcell && curformat) { + printf(alpm_list_getdata(curformat), alpm_list_getdata(curcell)); + curcell = alpm_list_next(curcell); + curformat = alpm_list_next(curformat); + } + + printf("\n"); +} + +/* creates format strings by checking max cell lengths in cols */ +static alpm_list_t *table_create_format(const alpm_list_t *header, + const alpm_list_t *rows) +{ + alpm_list_t *longest_str, *longest_strs = NULL; + alpm_list_t *formats = NULL; + const alpm_list_t *i, *row, *cell; + char *str, *formatstr; + const int padding = 2; + int colwidth, totalwidth = 0; + int curcol = 0; + + /* header determines column count and initial values of longest_strs */ + for(i = header; i; i = alpm_list_next(i)) { + longest_strs = alpm_list_add(longest_strs, alpm_list_getdata(i)); + } + + /* now find the longest string in each column */ + for(longest_str = longest_strs; longest_str; + longest_str = alpm_list_next(longest_str), curcol++) { + for(i = rows; i; i = alpm_list_next(i)) { + row = alpm_list_getdata(i); + cell = alpm_list_nth(row, curcol); + str = alpm_list_getdata(cell); + + if(strlen(str) > strlen(alpm_list_getdata(longest_str))) { + longest_str->data = str; + } + } + } + + /* now use the column width info to generate format strings */ + for(i = longest_strs; i; i = alpm_list_next(i)) { + const char *display; + colwidth = strlen(alpm_list_getdata(i)) + padding; + totalwidth += colwidth; + + /* right align the last column for a cleaner table display */ + display = (alpm_list_next(i) != NULL) ? "%%-%ds" : "%%%ds"; + pm_asprintf(&formatstr, display, colwidth); + + formats = alpm_list_add(formats, formatstr); + } + + alpm_list_free(longest_strs); + + /* return NULL if terminal is not wide enough */ + if(totalwidth > getcols()) { + fprintf(stderr, _("insufficient columns available for table display\n")); + FREELIST(formats); + return(NULL); + } + + return(formats); +} + +/** Displays the list in table format + * + * @param title the tables title + * @param header the column headers. column count is determined by the nr + * of headers + * @param rows the rows to display as a list of lists of strings. the outer + * list represents the rows, the inner list the cells (= columns) + * + * @return -1 if not enough terminal cols available, else 0 + */ +int table_display(const char *title, const alpm_list_t *header, + const alpm_list_t *rows) +{ + const alpm_list_t *i; + alpm_list_t *formats; + + if(rows == NULL || header == NULL) { + return(0); + } + + formats = table_create_format(header, rows); + if(formats == NULL) { + return(-1); + } + + if(title != NULL) { + printf("%s\n\n", title); + } + + table_print_line(header, formats); + printf("\n"); + + for(i = rows; i; i = alpm_list_next(i)) { + table_print_line(alpm_list_getdata(i), formats); + } + + FREELIST(formats); + return(0); +} + void list_display(const char *title, const alpm_list_t *list) { const alpm_list_t *i; @@ -464,7 +589,7 @@ void list_display(const char *title, const alpm_list_t *list) for (j = 1; j <= len; j++) { printf(" "); } - } else if (cols != len) { + } else if(cols != len) { /* 2 spaces are added if this is not the first element on a line. */ printf(" "); cols += 2; @@ -503,20 +628,74 @@ void list_display_linebreak(const char *title, const alpm_list_t *list) } } } + +/* creates a header row for use with table_display */ +static alpm_list_t *create_verbose_header(int install) +{ + alpm_list_t *res = NULL; + char *str; + + pm_asprintf(&str, "%s", _("Name")); + res = alpm_list_add(res, str); + pm_asprintf(&str, "%s", _("Old Version")); + res = alpm_list_add(res, str); + if(install) { + pm_asprintf(&str, "%s", _("New Version")); + res = alpm_list_add(res, str); + } + pm_asprintf(&str, "%s", _("Size")); + res = alpm_list_add(res, str); + + return(res); +} + +/* returns package info as list of strings */ +static alpm_list_t *create_verbose_row(pmpkg_t *pkg, int install) +{ + char *str; + double size; + const char *label; + alpm_list_t *ret = NULL; + pmdb_t *ldb = alpm_option_get_localdb(); + + /* a row consists of the package name, */ + pm_asprintf(&str, "%s", alpm_pkg_get_name(pkg)); + ret = alpm_list_add(ret, str); + + /* old and new versions */ + if(install) { + pmpkg_t *oldpkg = alpm_db_get_pkg(ldb, alpm_pkg_get_name(pkg)); + pm_asprintf(&str, "%s", + oldpkg != NULL ? alpm_pkg_get_version(oldpkg) : ""); + ret = alpm_list_add(ret, str); + } + + pm_asprintf(&str, "%s", alpm_pkg_get_version(pkg)); + ret = alpm_list_add(ret, str); + + /* and size */ + size = humanize_size(alpm_pkg_get_size(pkg), 'M', 1, &label); + pm_asprintf(&str, "%.2f %s", size, label); + ret = alpm_list_add(ret, str); + + return(ret); +} + /* prepare a list of pkgs to display */ void display_targets(const alpm_list_t *pkgs, int install) { char *str; + const char *title, *label; + double size; const alpm_list_t *i; off_t isize = 0, dlsize = 0; - double mbisize = 0.0, mbdlsize = 0.0; - alpm_list_t *targets = NULL; + alpm_list_t *j, *lp, *header = NULL, *targets = NULL; if(!pkgs) { return; } - printf("\n"); + /* gather pkg infos */ for(i = pkgs; i; i = alpm_list_next(i)) { pmpkg_t *pkg = alpm_list_getdata(i); @@ -525,54 +704,69 @@ void display_targets(const alpm_list_t *pkgs, int install) } isize += alpm_pkg_get_isize(pkg); - /* print the package size with the output if ShowSize option set */ - if(config->showsize) { - double mbsize = (double)alpm_pkg_get_size(pkg) / (1024.0 * 1024.0); - - pm_asprintf(&str, "%s-%s [%.2f MB]", alpm_pkg_get_name(pkg), - alpm_pkg_get_version(pkg), mbsize); + if(config->verbosepkglists) { + targets = alpm_list_add(targets, create_verbose_row(pkg, install)); } else { pm_asprintf(&str, "%s-%s", alpm_pkg_get_name(pkg), alpm_pkg_get_version(pkg)); + targets = alpm_list_add(targets, str); } - targets = alpm_list_add(targets, str); } - /* Convert byte sizes to MB */ - mbdlsize = (double)dlsize / (1024.0 * 1024.0); - mbisize = (double)isize / (1024.0 * 1024.0); + /* print to screen */ + title = install ? _("Targets (%d):") : _("Remove (%d):"); + pm_asprintf(&str, title, alpm_list_count(pkgs)); - if(install) { - pm_asprintf(&str, _("Targets (%d):"), alpm_list_count(targets)); + printf("\n"); + if(config->verbosepkglists) { + header = create_verbose_header(install); + if(table_display(str, header, targets) != 0) { + config->verbosepkglists = 0; + display_targets(pkgs, install); + goto out; + } + } else { list_display(str, targets); - free(str); - printf("\n"); + } + printf("\n"); - printf(_("Total Download Size: %.2f MB\n"), mbdlsize); + if(install) { + size = humanize_size(dlsize, 'M', 1, &label); + printf(_("Total Download Size: %.2f %s\n"), size, label); if(!(config->flags & PM_TRANS_FLAG_DOWNLOADONLY)) { - printf(_("Total Installed Size: %.2f MB\n"), mbisize); + size = humanize_size(isize, 'M', 1, &label); + printf(_("Total Installed Size: %.2f %s\n"), size, label); } } else { - pm_asprintf(&str, _("Remove (%d):"), alpm_list_count(targets)); - list_display(str, targets); - free(str); - printf("\n"); - - printf(_("Total Removed Size: %.2f MB\n"), mbisize); + size = humanize_size(isize, 'M', 1, &label); + printf(_("Total Removed Size: %.2f %s\n"), size, label); } - FREELIST(targets); +out: + /* cleanup */ + if(config->verbosepkglists) { + /* targets is a list of lists of strings, free inner lists here */ + for(j = alpm_list_first(targets); j; j = alpm_list_next(j)) { + lp = alpm_list_getdata(j); + FREELIST(lp); + } + alpm_list_free(targets); + FREELIST(header); + } else { + FREELIST(targets); + } + free(str); } static off_t pkg_get_size(pmpkg_t *pkg) { switch(config->op) { case PM_OP_SYNC: - return(alpm_pkg_download_size(pkg)); + return alpm_pkg_download_size(pkg); case PM_OP_UPGRADE: - return(alpm_pkg_get_size(pkg)); + return alpm_pkg_get_size(pkg); default: - return(alpm_pkg_get_isize(pkg)); + return alpm_pkg_get_isize(pkg); } } @@ -588,17 +782,55 @@ static char *pkg_get_location(pmpkg_t *pkg) if(dburl) { char *pkgurl = NULL; pm_asprintf(&pkgurl, "%s/%s", dburl, alpm_pkg_get_filename(pkg)); - return(pkgurl); + return pkgurl; } case PM_OP_UPGRADE: - return(strdup(alpm_pkg_get_filename(pkg))); + return strdup(alpm_pkg_get_filename(pkg)); default: string = NULL; pm_asprintf(&string, "%s-%s", alpm_pkg_get_name(pkg), alpm_pkg_get_version(pkg)); - return(string); + return string; } } +/** Converts sizes in bytes into human readable units. + * + * @param bytes the size in bytes + * @param target_unit '\0' or a short label. If equal to one of the short unit + * labels ('B', 'K', ...) bytes is converted to target_unit; if '\0', the first + * unit which will bring the value to below a threshold of 2048 will be chosen. + * @param long_labels whether to use short ("K") or long ("KiB") unit labels + * @param label will be set to the appropriate unit label + * + * @return the size in the appropriate unit + */ +double humanize_size(off_t bytes, const char target_unit, int long_labels, + const char **label) +{ + static const char *shortlabels[] = {"B", "K", "M", "G", "T", "P"}; + static const char *longlabels[] = {"B", "KiB", "MiB", "GiB", "TiB", "PiB"}; + static const int unitcount = sizeof(shortlabels) / sizeof(shortlabels[0]); + + const char **labels = long_labels ? longlabels : shortlabels; + double val = (double)bytes; + int index; + + for(index = 0; index < unitcount - 1; index++) { + if(target_unit != '\0' && shortlabels[index][0] == target_unit) { + break; + } else if(target_unit == '\0' && val <= 2048.0) { + break; + } + val /= 1024.0; + } + + if(label) { + *label = labels[index]; + } + + return(val); +} + void print_packages(const alpm_list_t *packages) { const alpm_list_t *i; @@ -657,7 +889,7 @@ void print_packages(const alpm_list_t *packages) * alpm "compare func" signature */ int str_cmp(const void *s1, const void *s2) { - return(strcmp(s1, s2)); + return strcmp(s1, s2); } void display_new_optdepends(pmpkg_t *oldpkg, pmpkg_t *newpkg) @@ -726,13 +958,13 @@ static int parseindex(char *s, int *val, int min, int max) if(n < min || n > max) { fprintf(stderr, _("Invalid value: %d is not between %d and %d\n"), n, min, max); - return(-1); + return -1; } *val = n; - return(0); + return 0; } else { fprintf(stderr, _("Invalid number: %s\n"), s); - return(-1); + return -1; } } @@ -746,7 +978,7 @@ static int multiselect_parse(char *array, int count, char *response) char *ends = NULL; char *starts = strtok_r(str, " ", &saveptr); - if (starts == NULL) { + if(starts == NULL) { break; } strtrim(starts); @@ -754,7 +986,7 @@ static int multiselect_parse(char *array, int count, char *response) if(len == 0) continue; - if (*starts == '^') { + if(*starts == '^') { starts++; len--; include = 0; @@ -773,14 +1005,14 @@ static int multiselect_parse(char *array, int count, char *response) } if(parseindex(starts, &start, 1, count) != 0) - return(-1); + return -1; if(!ends) { array[start-1] = include; } else { int d; if(parseindex(ends, &end, start, count) != 0) { - return(-1); + return -1; } for(d = start; d <= end; d++) { array[d-1] = include; @@ -788,7 +1020,7 @@ static int multiselect_parse(char *array, int count, char *response) } } - return(0); + return 0; } int multiselect_question(char *array, int count) @@ -815,6 +1047,8 @@ int multiselect_question(char *array, int count) break; } + flush_term_input(); + if(fgets(response, sizeof(response), stdin)) { strtrim(response); if(strlen(response) > 0) { @@ -826,7 +1060,7 @@ int multiselect_question(char *array, int count) } break; } - return(0); + return 0; } int select_question(int count) @@ -852,19 +1086,21 @@ int select_question(int count) break; } + flush_term_input(); + if(fgets(response, sizeof(response), stdin)) { strtrim(response); if(strlen(response) > 0) { int n; if(parseindex(response, &n, 1, count) != 0) continue; - return(n-1); + return (n - 1); } } break; } - return(preset-1); + return (preset - 1); } @@ -895,23 +1131,25 @@ static int question(short preset, char *fmt, va_list args) if(config->noconfirm) { fprintf(stream, "\n"); - return(preset); + return preset; } fflush(stream); + flush_term_input(); + if(fgets(response, sizeof(response), stdin)) { strtrim(response); if(strlen(response) == 0) { - return(preset); + return preset; } if(strcasecmp(response, _("Y")) == 0 || strcasecmp(response, _("YES")) == 0) { - return(1); - } else if (strcasecmp(response, _("N")) == 0 || strcasecmp(response, _("NO")) == 0) { - return(0); + return 1; + } else if(strcasecmp(response, _("N")) == 0 || strcasecmp(response, _("NO")) == 0) { + return 0; } } - return(0); + return 0; } int yesno(char *fmt, ...) @@ -923,7 +1161,7 @@ int yesno(char *fmt, ...) ret = question(1, fmt, args); va_end(args); - return(ret); + return ret; } int noyes(char *fmt, ...) @@ -935,7 +1173,7 @@ int noyes(char *fmt, ...) ret = question(0, fmt, args); va_end(args); - return(ret); + return ret; } int pm_printf(pmloglevel_t level, const char *format, ...) @@ -948,7 +1186,7 @@ int pm_printf(pmloglevel_t level, const char *format, ...) ret = pm_vfprintf(stdout, level, format, args); va_end(args); - return(ret); + return ret; } int pm_fprintf(FILE *stream, pmloglevel_t level, const char *format, ...) @@ -961,7 +1199,7 @@ int pm_fprintf(FILE *stream, pmloglevel_t level, const char *format, ...) ret = pm_vfprintf(stream, level, format, args); va_end(args); - return(ret); + return ret; } int pm_asprintf(char **string, const char *format, ...) @@ -977,7 +1215,7 @@ int pm_asprintf(char **string, const char *format, ...) } va_end(args); - return(ret); + return ret; } int pm_vasprintf(char **string, pmloglevel_t level, const char *format, va_list args) @@ -1013,7 +1251,7 @@ int pm_vasprintf(char **string, pmloglevel_t level, const char *format, va_list } free(msg); - return(ret); + return ret; } int pm_vfprintf(FILE *stream, pmloglevel_t level, const char *format, va_list args) @@ -1061,7 +1299,7 @@ int pm_vfprintf(FILE *stream, pmloglevel_t level, const char *format, va_list ar /* print the message using va_arg list */ ret = vfprintf(stream, format, args); - return(ret); + return ret; } #ifndef HAVE_STRNDUP @@ -1070,7 +1308,7 @@ static size_t strnlen(const char *s, size_t max) { register const char *p; for(p = s; *p && max--; ++p); - return(p - s); + return (p - s); } char *strndup(const char *s, size_t n) @@ -1078,11 +1316,11 @@ char *strndup(const char *s, size_t n) size_t len = strnlen(s, n); char *new = (char *) malloc(len + 1); - if (new == NULL) + if(new == NULL) return NULL; new[len] = '\0'; - return (char *) memcpy(new, s, len); + return (char *)memcpy(new, s, len); } #endif |