Remove redundant transaction size output
[pacman-ng.git] / src / pacman / util.c
blob79fb54dc3fab226a584bee8ec206d74d935b3fd5
1 /*
2 * util.c
4 * Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org>
5 * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "config.h"
23 #include <sys/types.h>
24 #include <sys/ioctl.h>
25 #include <sys/stat.h>
26 #include <sys/time.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <stdarg.h>
31 #include <stdint.h> /* intmax_t */
32 #include <string.h>
33 #include <errno.h>
34 #include <ctype.h>
35 #include <dirent.h>
36 #include <unistd.h>
37 #include <limits.h>
38 #include <wchar.h>
39 #ifdef HAVE_TERMIOS_H
40 #include <termios.h> /* tcflush */
41 #endif
43 #include <alpm.h>
44 #include <alpm_list.h>
46 /* pacman */
47 #include "util.h"
48 #include "conf.h"
49 #include "callback.h"
52 int trans_init(alpm_transflag_t flags, int check_valid)
54 int ret;
56 check_syncdbs(0, check_valid);
58 ret = alpm_trans_init(config->handle, flags);
59 if(ret == -1) {
60 trans_init_error();
61 return -1;
63 return 0;
66 void trans_init_error(void)
68 enum _alpm_errno_t err = alpm_errno(config->handle);
69 pm_fprintf(stderr, ALPM_LOG_ERROR, _("failed to init transaction (%s)\n"),
70 alpm_strerror(err));
71 if(err == ALPM_ERR_HANDLE_LOCK) {
72 fprintf(stderr, _(" if you're sure a package manager is not already\n"
73 " running, you can remove %s\n"),
74 alpm_option_get_lockfile(config->handle));
78 int trans_release(void)
80 if(alpm_trans_release(config->handle) == -1) {
81 pm_fprintf(stderr, ALPM_LOG_ERROR, _("failed to release transaction (%s)\n"),
82 alpm_strerror(alpm_errno(config->handle)));
83 return -1;
85 return 0;
88 int needs_root(void)
90 switch(config->op) {
91 case PM_OP_DATABASE:
92 return 1;
93 case PM_OP_UPGRADE:
94 case PM_OP_REMOVE:
95 return !config->print;
96 case PM_OP_SYNC:
97 return (config->op_s_clean || config->op_s_sync ||
98 (!config->group && !config->op_s_info && !config->op_q_list &&
99 !config->op_s_search && !config->print));
100 default:
101 return 0;
105 int check_syncdbs(size_t need_repos, int check_valid)
107 int ret = 0;
108 alpm_list_t *i;
109 alpm_list_t *sync_dbs = alpm_option_get_syncdbs(config->handle);
111 if(need_repos && sync_dbs == NULL) {
112 pm_printf(ALPM_LOG_ERROR, _("no usable package repositories configured.\n"));
113 return 1;
116 if(check_valid) {
117 /* ensure all known dbs are valid */
118 for(i = sync_dbs; i; i = alpm_list_next(i)) {
119 alpm_db_t *db = i->data;
120 if(alpm_db_get_valid(db)) {
121 pm_printf(ALPM_LOG_ERROR, _("database '%s' is not valid (%s)\n"),
122 alpm_db_get_name(db), alpm_strerror(alpm_errno(config->handle)));
123 ret = 1;
127 return ret;
130 /* discard unhandled input on the terminal's input buffer */
131 static int flush_term_input(void) {
132 #ifdef HAVE_TCFLUSH
133 if(isatty(fileno(stdin))) {
134 return tcflush(fileno(stdin), TCIFLUSH);
136 #endif
138 /* fail silently */
139 return 0;
142 /* gets the current screen column width */
143 unsigned short getcols(void)
145 const unsigned short default_tty = 80;
146 const unsigned short default_notty = 0;
147 unsigned short termwidth = 0;
149 if(!isatty(fileno(stdout))) {
150 return default_notty;
153 #ifdef TIOCGSIZE
154 struct ttysize win;
155 if(ioctl(1, TIOCGSIZE, &win) == 0) {
156 termwidth = win.ts_cols;
158 #elif defined(TIOCGWINSZ)
159 struct winsize win;
160 if(ioctl(1, TIOCGWINSZ, &win) == 0) {
161 termwidth = win.ws_col;
163 #endif
164 return termwidth == 0 ? default_tty : termwidth;
167 /* does the same thing as 'rm -rf' */
168 int rmrf(const char *path)
170 int errflag = 0;
171 struct dirent *dp;
172 DIR *dirp;
174 if(!unlink(path)) {
175 return 0;
176 } else {
177 if(errno == ENOENT) {
178 return 0;
179 } else if(errno == EPERM) {
180 /* fallthrough */
181 } else if(errno == EISDIR) {
182 /* fallthrough */
183 } else if(errno == ENOTDIR) {
184 return 1;
185 } else {
186 /* not a directory */
187 return 1;
190 dirp = opendir(path);
191 if(!dirp) {
192 return 1;
194 for(dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
195 if(dp->d_ino) {
196 char name[PATH_MAX];
197 sprintf(name, "%s/%s", path, dp->d_name);
198 if(strcmp(dp->d_name, "..") != 0 && strcmp(dp->d_name, ".") != 0) {
199 errflag += rmrf(name);
203 closedir(dirp);
204 if(rmdir(path)) {
205 errflag++;
207 return errflag;
211 /** Parse the basename of a program from a path.
212 * @param path path to parse basename from
214 * @return everything following the final '/'
216 const char *mbasename(const char *path)
218 const char *last = strrchr(path, '/');
219 if(last) {
220 return last + 1;
222 return path;
225 /** Parse the dirname of a program from a path.
226 * The path returned should be freed.
227 * @param path path to parse dirname from
229 * @return everything preceding the final '/'
231 char *mdirname(const char *path)
233 char *ret, *last;
235 /* null or empty path */
236 if(path == NULL || path == '\0') {
237 return strdup(".");
240 ret = strdup(path);
241 last = strrchr(ret, '/');
243 if(last != NULL) {
244 /* we found a '/', so terminate our string */
245 *last = '\0';
246 return ret;
248 /* no slash found */
249 free(ret);
250 return strdup(".");
253 /* output a string, but wrap words properly with a specified indentation
255 void indentprint(const char *str, size_t indent)
257 wchar_t *wcstr;
258 const wchar_t *p;
259 int len, cidx;
260 const unsigned short cols = getcols();
262 if(!str) {
263 return;
266 /* if we're not a tty, or our tty is not wide enough that wrapping even makes
267 * sense, print without indenting */
268 if(cols == 0 || indent > cols) {
269 printf("%s", str);
270 return;
273 len = strlen(str) + 1;
274 wcstr = calloc(len, sizeof(wchar_t));
275 len = mbstowcs(wcstr, str, len);
276 p = wcstr;
277 cidx = indent;
279 if(!p || !len) {
280 return;
283 while(*p) {
284 if(*p == L' ') {
285 const wchar_t *q, *next;
286 p++;
287 if(p == NULL || *p == L' ') continue;
288 next = wcschr(p, L' ');
289 if(next == NULL) {
290 next = p + wcslen(p);
292 /* len captures # cols */
293 len = 0;
294 q = p;
295 while(q < next) {
296 len += wcwidth(*q++);
298 if(len > (cols - cidx - 1)) {
299 /* wrap to a newline and reindent */
300 printf("\n%-*s", (int)indent, "");
301 cidx = indent;
302 } else {
303 printf(" ");
304 cidx++;
306 continue;
308 printf("%lc", (wint_t)*p);
309 cidx += wcwidth(*p);
310 p++;
312 free(wcstr);
315 /* Convert a string to uppercase
317 char *strtoupper(char *str)
319 char *ptr = str;
321 while(*ptr) {
322 (*ptr) = (char)toupper((unsigned char)*ptr);
323 ptr++;
325 return str;
328 /* Trim whitespace and newlines from a string
330 char *strtrim(char *str)
332 char *pch = str;
334 if(str == NULL || *str == '\0') {
335 /* string is empty, so we're done. */
336 return str;
339 while(isspace((unsigned char)*pch)) {
340 pch++;
342 if(pch != str) {
343 size_t len = strlen(pch);
344 if(len) {
345 memmove(str, pch, len + 1);
346 } else {
347 *str = '\0';
351 /* check if there wasn't anything but whitespace in the string. */
352 if(*str == '\0') {
353 return str;
356 pch = (str + (strlen(str) - 1));
357 while(isspace((unsigned char)*pch)) {
358 pch--;
360 *++pch = '\0';
362 return str;
365 /* Replace all occurances of 'needle' with 'replace' in 'str', returning
366 * a new string (must be free'd) */
367 char *strreplace(const char *str, const char *needle, const char *replace)
369 const char *p = NULL, *q = NULL;
370 char *newstr = NULL, *newp = NULL;
371 alpm_list_t *i = NULL, *list = NULL;
372 size_t needlesz = strlen(needle), replacesz = strlen(replace);
373 size_t newsz;
375 if(!str) {
376 return NULL;
379 p = str;
380 q = strstr(p, needle);
381 while(q) {
382 list = alpm_list_add(list, (char *)q);
383 p = q + needlesz;
384 q = strstr(p, needle);
387 /* no occurences of needle found */
388 if(!list) {
389 return strdup(str);
391 /* size of new string = size of old string + "number of occurences of needle"
392 * x "size difference between replace and needle" */
393 newsz = strlen(str) + 1 +
394 alpm_list_count(list) * (replacesz - needlesz);
395 newstr = calloc(newsz, sizeof(char));
396 if(!newstr) {
397 return NULL;
400 p = str;
401 newp = newstr;
402 for(i = list; i; i = alpm_list_next(i)) {
403 q = alpm_list_getdata(i);
404 if(q > p) {
405 /* add chars between this occurence and last occurence, if any */
406 memcpy(newp, p, (size_t)(q - p));
407 newp += q - p;
409 memcpy(newp, replace, replacesz);
410 newp += replacesz;
411 p = q + needlesz;
413 alpm_list_free(list);
415 if(*p) {
416 /* add the rest of 'p' */
417 strcpy(newp, p);
420 return newstr;
423 /** Splits a string into a list of strings using the chosen character as
424 * a delimiter.
426 * @param str the string to split
427 * @param splitchar the character to split at
429 * @return a list containing the duplicated strings
431 alpm_list_t *strsplit(const char *str, const char splitchar)
433 alpm_list_t *list = NULL;
434 const char *prev = str;
435 char *dup = NULL;
437 while((str = strchr(str, splitchar))) {
438 dup = strndup(prev, (size_t)(str - prev));
439 if(dup == NULL) {
440 return NULL;
442 list = alpm_list_add(list, dup);
444 str++;
445 prev = str;
448 dup = strdup(prev);
449 if(dup == NULL) {
450 return NULL;
452 list = alpm_list_add(list, dup);
454 return list;
457 static size_t string_length(const char *s)
459 int len;
460 wchar_t *wcstr;
462 if(!s || s[0] == '\0') {
463 return 0;
465 /* len goes from # bytes -> # chars -> # cols */
466 len = strlen(s) + 1;
467 wcstr = calloc(len, sizeof(wchar_t));
468 len = mbstowcs(wcstr, s, len);
469 len = wcswidth(wcstr, len);
470 free(wcstr);
472 return len;
475 void string_display(const char *title, const char *string)
477 if(title) {
478 printf("%s ", title);
480 if(string == NULL || string[0] == '\0') {
481 printf(_("None"));
482 } else {
483 /* compute the length of title + a space */
484 size_t len = string_length(title) + 1;
485 indentprint(string, len);
487 printf("\n");
490 static void table_print_line(const alpm_list_t *line,
491 const alpm_list_t *formats)
493 const alpm_list_t *curformat = formats;
494 const alpm_list_t *curcell = line;
496 while(curcell && curformat) {
497 printf(alpm_list_getdata(curformat), alpm_list_getdata(curcell));
498 curcell = alpm_list_next(curcell);
499 curformat = alpm_list_next(curformat);
502 printf("\n");
505 /* creates format strings by checking max cell lengths in cols */
506 static alpm_list_t *table_create_format(const alpm_list_t *header,
507 const alpm_list_t *rows)
509 alpm_list_t *formats = NULL;
510 const alpm_list_t *i;
511 const unsigned short padding = 2;
512 size_t curcol, totalcols, totalwidth = 0;
513 size_t *colwidths;
515 totalcols = alpm_list_count(header);
516 colwidths = malloc(totalcols * sizeof(size_t));
517 if(!colwidths) {
518 return NULL;
520 /* header determines column count and initial values of longest_strs */
521 for(i = header, curcol = 0; i; i = alpm_list_next(i), curcol++) {
522 colwidths[curcol] = string_length(alpm_list_getdata(i));
525 /* now find the longest string in each column */
526 for(i = rows; i; i = alpm_list_next(i)) {
527 /* grab first column of each row and iterate through columns */
528 const alpm_list_t *j = alpm_list_getdata(i);
529 for(curcol = 0; j; j = alpm_list_next(j), curcol++) {
530 char *str = alpm_list_getdata(j);
531 size_t str_len = string_length(str);
533 if(str_len > colwidths[curcol]) {
534 colwidths[curcol] = str_len;
539 /* now use the column width info to generate format strings */
540 for(curcol = 0; curcol < totalcols; curcol++) {
541 const char *display;
542 char *formatstr;
543 size_t colwidth = colwidths[curcol] + padding;
544 totalwidth += colwidth;
546 /* right align the last column for a cleaner table display */
547 display = (curcol + 1 < totalcols) ? "%%-%ds" : "%%%ds";
548 pm_asprintf(&formatstr, display, colwidth);
550 formats = alpm_list_add(formats, formatstr);
553 free(colwidths);
555 /* return NULL if terminal is not wide enough */
556 if(totalwidth > getcols()) {
557 fprintf(stderr, _("insufficient columns available for table display\n"));
558 FREELIST(formats);
559 return NULL;
562 return formats;
565 /** Displays the list in table format
567 * @param title the tables title
568 * @param header the column headers. column count is determined by the nr
569 * of headers
570 * @param rows the rows to display as a list of lists of strings. the outer
571 * list represents the rows, the inner list the cells (= columns)
573 * @return -1 if not enough terminal cols available, else 0
575 int table_display(const char *title, const alpm_list_t *header,
576 const alpm_list_t *rows)
578 const alpm_list_t *i;
579 alpm_list_t *formats;
581 if(rows == NULL || header == NULL) {
582 return 0;
585 formats = table_create_format(header, rows);
586 if(formats == NULL) {
587 return -1;
590 if(title != NULL) {
591 printf("%s\n\n", title);
594 table_print_line(header, formats);
595 printf("\n");
597 for(i = rows; i; i = alpm_list_next(i)) {
598 table_print_line(alpm_list_getdata(i), formats);
601 FREELIST(formats);
602 return 0;
605 void list_display(const char *title, const alpm_list_t *list)
607 const alpm_list_t *i;
608 size_t len = 0;
610 if(title) {
611 len = string_length(title) + 1;
612 printf("%s ", title);
615 if(!list) {
616 printf("%s\n", _("None"));
617 } else {
618 const unsigned short maxcols = getcols();
619 size_t cols = len;
620 const char *str = alpm_list_getdata(list);
621 printf("%s", str);
622 cols += string_length(str);
623 for(i = alpm_list_next(list); i; i = alpm_list_next(i)) {
624 str = alpm_list_getdata(i);
625 size_t s = string_length(str);
626 /* wrap only if we have enough usable column space */
627 if(maxcols > len && cols + s + 2 >= maxcols) {
628 size_t j;
629 cols = len;
630 printf("\n");
631 for (j = 1; j <= len; j++) {
632 printf(" ");
634 } else if(cols != len) {
635 /* 2 spaces are added if this is not the first element on a line. */
636 printf(" ");
637 cols += 2;
639 printf("%s", str);
640 cols += s;
642 printf("\n");
646 void list_display_linebreak(const char *title, const alpm_list_t *list)
648 size_t len = 0;
650 if(title) {
651 len = string_length(title) + 1;
652 printf("%s ", title);
655 if(!list) {
656 printf("%s\n", _("None"));
657 } else {
658 const alpm_list_t *i;
659 /* Print the first element */
660 indentprint((const char *) alpm_list_getdata(list), len);
661 printf("\n");
662 /* Print the rest */
663 for(i = alpm_list_next(list); i; i = alpm_list_next(i)) {
664 size_t j;
665 for(j = 1; j <= len; j++) {
666 printf(" ");
668 indentprint((const char *) alpm_list_getdata(i), len);
669 printf("\n");
674 void signature_display(const char *title, alpm_siglist_t *siglist)
676 size_t len = 0;
678 if(title) {
679 len = string_length(title) + 1;
680 printf("%s ", title);
682 if(siglist->count == 0) {
683 printf(_("None"));
684 } else {
685 size_t i;
686 for(i = 0; i < siglist->count; i++) {
687 char *sigline;
688 const char *status, *validity, *name;
689 int ret;
690 alpm_sigresult_t *result = siglist->results + i;
691 /* Don't re-indent the first result */
692 if(i != 0) {
693 size_t j;
694 for(j = 1; j <= len; j++) {
695 printf(" ");
698 switch(result->status) {
699 case ALPM_SIGSTATUS_VALID:
700 status = _("Valid");
701 break;
702 case ALPM_SIGSTATUS_KEY_EXPIRED:
703 status = _("Key expired");
704 break;
705 case ALPM_SIGSTATUS_SIG_EXPIRED:
706 status = _("Expired");
707 break;
708 case ALPM_SIGSTATUS_INVALID:
709 status = _("Invalid");
710 break;
711 case ALPM_SIGSTATUS_KEY_UNKNOWN:
712 status = _("Key unknown");
713 break;
714 case ALPM_SIGSTATUS_KEY_DISABLED:
715 status = _("Key disabled");
716 break;
717 default:
718 status = _("Signature error");
719 break;
721 switch(result->validity) {
722 case ALPM_SIGVALIDITY_FULL:
723 validity = _("full trust");
724 break;
725 case ALPM_SIGVALIDITY_MARGINAL:
726 validity = _("marginal trust");
727 break;
728 case ALPM_SIGVALIDITY_NEVER:
729 validity = _("never trust");
730 break;
731 case ALPM_SIGVALIDITY_UNKNOWN:
732 default:
733 validity = _("unknown trust");
734 break;
736 name = result->key.uid ? result->key.uid : result->key.fingerprint;
737 ret = pm_asprintf(&sigline, _("%s, %s from \"%s\""),
738 status, validity, name);
739 if(ret == -1) {
740 pm_fprintf(stderr, ALPM_LOG_ERROR, _("failed to allocate string\n"));
741 continue;
743 indentprint(sigline, len);
744 printf("\n");
745 free(sigline);
750 /* creates a header row for use with table_display */
751 static alpm_list_t *create_verbose_header(int dl_size)
753 alpm_list_t *res = NULL;
754 char *str;
756 str = _("Name");
757 res = alpm_list_add(res, str);
758 str = _("Old Version");
759 res = alpm_list_add(res, str);
760 str = _("New Version");
761 res = alpm_list_add(res, str);
762 str = _("Net Change");
763 res = alpm_list_add(res, str);
764 if(dl_size) {
765 str = _("Download Size");
766 res = alpm_list_add(res, str);
769 return res;
772 /* returns package info as list of strings */
773 static alpm_list_t *create_verbose_row(pm_target_t *target, int dl_size)
775 char *str;
776 off_t size = 0;
777 double human_size;
778 const char *label;
779 alpm_list_t *ret = NULL;
781 /* a row consists of the package name, */
782 if(target->install) {
783 pm_asprintf(&str, "%s", alpm_pkg_get_name(target->install));
784 } else {
785 pm_asprintf(&str, "%s", alpm_pkg_get_name(target->remove));
787 ret = alpm_list_add(ret, str);
789 /* old and new versions */
790 pm_asprintf(&str, "%s",
791 target->remove != NULL ? alpm_pkg_get_version(target->remove) : "");
792 ret = alpm_list_add(ret, str);
794 pm_asprintf(&str, "%s",
795 target->install != NULL ? alpm_pkg_get_version(target->install) : "");
796 ret = alpm_list_add(ret, str);
798 /* and size */
799 size -= target->remove ? alpm_pkg_get_isize(target->remove) : 0;
800 size += target->install ? alpm_pkg_get_isize(target->install) : 0;
801 human_size = humanize_size(size, 'M', &label);
802 pm_asprintf(&str, "%.2f %s", human_size, label);
803 ret = alpm_list_add(ret, str);
805 if(dl_size) {
806 size = target->install ? alpm_pkg_download_size(target->install) : 0;
807 human_size = humanize_size(size, 'M', &label);
808 if(size != 0) {
809 pm_asprintf(&str, "%.2f %s", human_size, label);
810 } else {
811 str = strdup("");
813 ret = alpm_list_add(ret, str);
816 return ret;
819 /* prepare a list of pkgs to display */
820 static void _display_targets(alpm_list_t *targets, int verbose)
822 char *str;
823 const char *label;
824 double size;
825 off_t isize = 0, rsize = 0, dlsize = 0;
826 alpm_list_t *i, *rows = NULL, *names = NULL;
827 int show_dl_size = config->op == PM_OP_SYNC;
829 if(!targets) {
830 return;
833 /* gather package info */
834 for(i = targets; i; i = alpm_list_next(i)) {
835 pm_target_t *target = alpm_list_getdata(i);
837 if(target->install) {
838 dlsize += alpm_pkg_download_size(target->install);
839 isize += alpm_pkg_get_isize(target->install);
841 if(target->remove) {
842 /* add up size of all removed packages */
843 rsize += alpm_pkg_get_isize(target->remove);
846 /* form data for both verbose and non-verbose display */
847 rows = alpm_list_add(rows, create_verbose_row(target, show_dl_size));
848 if(target->install) {
849 pm_asprintf(&str, "%s-%s", alpm_pkg_get_name(target->install),
850 alpm_pkg_get_version(target->install));
851 } else if(isize == 0) {
852 pm_asprintf(&str, "%s-%s", alpm_pkg_get_name(target->remove),
853 alpm_pkg_get_version(target->remove));
854 } else {
855 pm_asprintf(&str, "%s-%s [removal]", alpm_pkg_get_name(target->remove),
856 alpm_pkg_get_version(target->remove));
858 names = alpm_list_add(names, str);
861 /* print to screen */
862 pm_asprintf(&str, _("Targets (%d):"), alpm_list_count(targets));
864 printf("\n");
865 if(verbose) {
866 alpm_list_t *header = create_verbose_header(show_dl_size);
867 if(table_display(str, header, rows) != 0) {
868 /* fallback to list display if table wouldn't fit */
869 list_display(str, names);
871 alpm_list_free(header);
872 } else {
873 list_display(str, names);
875 printf("\n");
877 /* rows is a list of lists of strings, free inner lists here */
878 for(i = rows; i; i = alpm_list_next(i)) {
879 alpm_list_t *lp = alpm_list_getdata(i);
880 FREELIST(lp);
882 alpm_list_free(rows);
883 FREELIST(names);
884 free(str);
886 if(dlsize > 0 || config->op_s_downloadonly) {
887 size = humanize_size(dlsize, 'M', &label);
888 printf(_("Total Download Size: %.2f %s\n"), size, label);
890 if(!config->op_s_downloadonly) {
891 if(isize > 0) {
892 size = humanize_size(isize, 'M', &label);
893 printf(_("Total Installed Size: %.2f %s\n"), size, label);
895 if(rsize > 0 && isize == 0) {
896 size = humanize_size(rsize, 'M', &label);
897 printf(_("Total Removed Size: %.2f %s\n"), size, label);
899 /* only show this net value if different from raw installed size */
900 if(isize > 0 && rsize > 0) {
901 size = humanize_size(isize - rsize, 'M', &label);
902 printf(_("Net Upgrade Size: %.2f %s\n"), size, label);
907 static int target_cmp(const void *p1, const void *p2)
909 const pm_target_t *targ1 = p1;
910 const pm_target_t *targ2 = p2;
911 /* explicit are always sorted after implicit (e.g. deps, pulled targets) */
912 if(targ1->is_explicit != targ2->is_explicit) {
913 return targ1->is_explicit > targ2->is_explicit;
915 const char *name1 = targ1->install ?
916 alpm_pkg_get_name(targ1->install) : alpm_pkg_get_name(targ1->remove);
917 const char *name2 = targ2->install ?
918 alpm_pkg_get_name(targ2->install) : alpm_pkg_get_name(targ2->remove);
919 return strcmp(name1, name2);
922 static int pkg_cmp(const void *p1, const void *p2)
924 /* explicit cast due to (un)necessary removal of const */
925 alpm_pkg_t *pkg1 = (alpm_pkg_t *)p1;
926 alpm_pkg_t *pkg2 = (alpm_pkg_t *)p2;
927 return strcmp(alpm_pkg_get_name(pkg1), alpm_pkg_get_name(pkg2));
930 void display_targets(void)
932 alpm_list_t *i, *targets = NULL;
933 alpm_db_t *db_local = alpm_option_get_localdb(config->handle);
935 for(i = alpm_trans_get_add(config->handle); i; i = alpm_list_next(i)) {
936 alpm_pkg_t *pkg = alpm_list_getdata(i);
937 pm_target_t *targ = calloc(1, sizeof(pm_target_t));
938 if(!targ) return;
939 targ->install = pkg;
940 targ->remove = alpm_db_get_pkg(db_local, alpm_pkg_get_name(pkg));
941 if(alpm_list_find(config->explicit_adds, pkg, pkg_cmp)) {
942 targ->is_explicit = 1;
944 targets = alpm_list_add(targets, targ);
946 for(i = alpm_trans_get_remove(config->handle); i; i = alpm_list_next(i)) {
947 alpm_pkg_t *pkg = alpm_list_getdata(i);
948 pm_target_t *targ = calloc(1, sizeof(pm_target_t));
949 if(!targ) return;
950 targ->remove = pkg;
951 if(alpm_list_find(config->explicit_removes, pkg, pkg_cmp)) {
952 targ->is_explicit = 1;
954 targets = alpm_list_add(targets, targ);
957 targets = alpm_list_msort(targets, alpm_list_count(targets), target_cmp);
958 _display_targets(targets, config->verbosepkglists);
959 FREELIST(targets);
962 static off_t pkg_get_size(alpm_pkg_t *pkg)
964 switch(config->op) {
965 case PM_OP_SYNC:
966 return alpm_pkg_download_size(pkg);
967 case PM_OP_UPGRADE:
968 return alpm_pkg_get_size(pkg);
969 default:
970 return alpm_pkg_get_isize(pkg);
974 static char *pkg_get_location(alpm_pkg_t *pkg)
976 alpm_list_t *servers;
977 char *string = NULL;
978 switch(config->op) {
979 case PM_OP_SYNC:
980 servers = alpm_db_get_servers(alpm_pkg_get_db(pkg));
981 if(servers) {
982 pm_asprintf(&string, "%s/%s", alpm_list_getdata(servers),
983 alpm_pkg_get_filename(pkg));
984 return string;
986 case PM_OP_UPGRADE:
987 return strdup(alpm_pkg_get_filename(pkg));
988 default:
989 pm_asprintf(&string, "%s-%s", alpm_pkg_get_name(pkg), alpm_pkg_get_version(pkg));
990 return string;
994 /** Converts sizes in bytes into human readable units.
996 * @param bytes the size in bytes
997 * @param target_unit '\0' or a short label. If equal to one of the short unit
998 * labels ('B', 'K', ...) bytes is converted to target_unit; if '\0', the first
999 * unit which will bring the value to below a threshold of 2048 will be chosen.
1000 * @param long_labels whether to use short ("K") or long ("KiB") unit labels
1001 * @param label will be set to the appropriate unit label
1003 * @return the size in the appropriate unit
1005 double humanize_size(off_t bytes, const char target_unit, const char **label)
1007 static const char *labels[] = {"B", "KiB", "MiB", "GiB",
1008 "TiB", "PiB", "EiB", "ZiB", "YiB"};
1009 static const int unitcount = sizeof(labels) / sizeof(labels[0]);
1011 double val = (double)bytes;
1012 int index;
1014 for(index = 0; index < unitcount - 1; index++) {
1015 if(target_unit != '\0' && labels[index][0] == target_unit) {
1016 break;
1017 } else if(target_unit == '\0' && val <= 2048.0 && val >= -2048.0) {
1018 break;
1020 val /= 1024.0;
1023 if(label) {
1024 *label = labels[index];
1027 return val;
1030 void print_packages(const alpm_list_t *packages)
1032 const alpm_list_t *i;
1033 if(!config->print_format) {
1034 config->print_format = strdup("%l");
1036 for(i = packages; i; i = alpm_list_next(i)) {
1037 alpm_pkg_t *pkg = alpm_list_getdata(i);
1038 char *string = strdup(config->print_format);
1039 char *temp = string;
1040 /* %n : pkgname */
1041 if(strstr(temp,"%n")) {
1042 string = strreplace(temp, "%n", alpm_pkg_get_name(pkg));
1043 free(temp);
1044 temp = string;
1046 /* %v : pkgver */
1047 if(strstr(temp,"%v")) {
1048 string = strreplace(temp, "%v", alpm_pkg_get_version(pkg));
1049 free(temp);
1050 temp = string;
1052 /* %l : location */
1053 if(strstr(temp,"%l")) {
1054 char *pkgloc = pkg_get_location(pkg);
1055 string = strreplace(temp, "%l", pkgloc);
1056 free(pkgloc);
1057 free(temp);
1058 temp = string;
1060 /* %r : repo */
1061 if(strstr(temp,"%r")) {
1062 const char *repo = "local";
1063 alpm_db_t *db = alpm_pkg_get_db(pkg);
1064 if(db) {
1065 repo = alpm_db_get_name(db);
1067 string = strreplace(temp, "%r", repo);
1068 free(temp);
1069 temp = string;
1071 /* %s : size */
1072 if(strstr(temp,"%s")) {
1073 char *size;
1074 pm_asprintf(&size, "%jd", (intmax_t)pkg_get_size(pkg));
1075 string = strreplace(temp, "%s", size);
1076 free(size);
1077 free(temp);
1079 printf("%s\n",string);
1080 free(string);
1084 /* Helper function for comparing strings using the
1085 * alpm "compare func" signature */
1086 int str_cmp(const void *s1, const void *s2)
1088 return strcmp(s1, s2);
1091 void display_new_optdepends(alpm_pkg_t *oldpkg, alpm_pkg_t *newpkg)
1093 alpm_list_t *old = alpm_pkg_get_optdepends(oldpkg);
1094 alpm_list_t *new = alpm_pkg_get_optdepends(newpkg);
1095 alpm_list_t *optdeps = alpm_list_diff(new,old,str_cmp);
1096 if(optdeps) {
1097 printf(_("New optional dependencies for %s\n"), alpm_pkg_get_name(newpkg));
1098 list_display_linebreak(" ", optdeps);
1100 alpm_list_free(optdeps);
1103 void display_optdepends(alpm_pkg_t *pkg)
1105 alpm_list_t *optdeps = alpm_pkg_get_optdepends(pkg);
1106 if(optdeps) {
1107 printf(_("Optional dependencies for %s\n"), alpm_pkg_get_name(pkg));
1108 list_display_linebreak(" ", optdeps);
1112 static void display_repo_list(const char *dbname, alpm_list_t *list)
1114 const char *prefix= " ";
1116 printf(":: ");
1117 printf(_("Repository %s\n"), dbname);
1118 list_display(prefix, list);
1121 void select_display(const alpm_list_t *pkglist)
1123 const alpm_list_t *i;
1124 int nth = 1;
1125 alpm_list_t *list = NULL;
1126 char *string = NULL;
1127 const char *dbname = NULL;
1129 for (i = pkglist; i; i = i->next) {
1130 alpm_pkg_t *pkg = alpm_list_getdata(i);
1131 alpm_db_t *db = alpm_pkg_get_db(pkg);
1133 if(!dbname)
1134 dbname = alpm_db_get_name(db);
1135 if(strcmp(alpm_db_get_name(db), dbname) != 0) {
1136 display_repo_list(dbname, list);
1137 FREELIST(list);
1138 dbname = alpm_db_get_name(db);
1140 string = NULL;
1141 pm_asprintf(&string, "%d) %s", nth, alpm_pkg_get_name(pkg));
1142 list = alpm_list_add(list, string);
1143 nth++;
1145 display_repo_list(dbname, list);
1146 FREELIST(list);
1149 static int parseindex(char *s, int *val, int min, int max)
1151 char *endptr = NULL;
1152 int n = strtol(s, &endptr, 10);
1153 if(*endptr == '\0') {
1154 if(n < min || n > max) {
1155 fprintf(stderr, _("Invalid value: %d is not between %d and %d\n"),
1156 n, min, max);
1157 return -1;
1159 *val = n;
1160 return 0;
1161 } else {
1162 fprintf(stderr, _("Invalid number: %s\n"), s);
1163 return -1;
1167 static int multiselect_parse(char *array, int count, char *response)
1169 char *str, *saveptr;
1171 for (str = response; ; str = NULL) {
1172 int include = 1;
1173 int start, end;
1174 char *ends = NULL;
1175 char *starts = strtok_r(str, " ", &saveptr);
1177 if(starts == NULL) {
1178 break;
1180 strtrim(starts);
1181 int len = strlen(starts);
1182 if(len == 0)
1183 continue;
1185 if(*starts == '^') {
1186 starts++;
1187 len--;
1188 include = 0;
1189 } else if(str) {
1190 /* if first token is including, we unselect all targets */
1191 memset(array, 0, count);
1194 if(len > 1) {
1195 /* check for range */
1196 char *p;
1197 if((p = strchr(starts + 1, '-'))) {
1198 *p = 0;
1199 ends = p + 1;
1203 if(parseindex(starts, &start, 1, count) != 0)
1204 return -1;
1206 if(!ends) {
1207 array[start-1] = include;
1208 } else {
1209 int d;
1210 if(parseindex(ends, &end, start, count) != 0) {
1211 return -1;
1213 for(d = start; d <= end; d++) {
1214 array[d-1] = include;
1219 return 0;
1222 int multiselect_question(char *array, int count)
1224 char *response, *lastchar;
1225 FILE *stream;
1226 size_t response_len = 64;
1228 if(config->noconfirm) {
1229 stream = stdout;
1230 } else {
1231 /* Use stderr so questions are always displayed when redirecting output */
1232 stream = stderr;
1235 response = malloc(response_len);
1236 if(!response) {
1237 return -1;
1239 lastchar = response + response_len - 1;
1240 /* sentinel byte to later see if we filled up the entire string */
1241 *lastchar = 1;
1243 while(1) {
1244 memset(array, 1, count);
1246 fprintf(stream, "\n");
1247 fprintf(stream, _("Enter a selection (default=all)"));
1248 fprintf(stream, ": ");
1249 fflush(stream);
1251 if(config->noconfirm) {
1252 fprintf(stream, "\n");
1253 break;
1256 flush_term_input();
1258 if(fgets(response, response_len, stdin)) {
1259 const size_t response_incr = 64;
1260 /* handle buffer not being large enough to read full line case */
1261 while(*lastchar == '\0' && lastchar[-1] != '\n') {
1262 response_len += response_incr;
1263 response = realloc(response, response_len);
1264 if(!response) {
1265 return -1;
1267 lastchar = response + response_len - 1;
1268 /* sentinel byte */
1269 *lastchar = 1;
1270 if(fgets(response + response_len - response_incr - 1,
1271 response_incr + 1, stdin) == 0) {
1272 free(response);
1273 return -1;
1276 strtrim(response);
1277 if(strlen(response) > 0) {
1278 if(multiselect_parse(array, count, response) == -1) {
1279 /* only loop if user gave an invalid answer */
1280 continue;
1283 break;
1284 } else {
1285 free(response);
1286 return -1;
1290 free(response);
1291 return 0;
1294 int select_question(int count)
1296 char response[32];
1297 FILE *stream;
1298 int preset = 1;
1300 if(config->noconfirm) {
1301 stream = stdout;
1302 } else {
1303 /* Use stderr so questions are always displayed when redirecting output */
1304 stream = stderr;
1307 while(1) {
1308 fprintf(stream, "\n");
1309 fprintf(stream, _("Enter a number (default=%d)"), preset);
1310 fprintf(stream, ": ");
1312 if(config->noconfirm) {
1313 fprintf(stream, "\n");
1314 break;
1317 flush_term_input();
1319 if(fgets(response, sizeof(response), stdin)) {
1320 strtrim(response);
1321 if(strlen(response) > 0) {
1322 int n;
1323 if(parseindex(response, &n, 1, count) != 0)
1324 continue;
1325 return (n - 1);
1328 break;
1331 return (preset - 1);
1335 /* presents a prompt and gets a Y/N answer */
1336 static int question(short preset, char *fmt, va_list args)
1338 char response[32];
1339 FILE *stream;
1341 if(config->noconfirm) {
1342 stream = stdout;
1343 } else {
1344 /* Use stderr so questions are always displayed when redirecting output */
1345 stream = stderr;
1348 /* ensure all text makes it to the screen before we prompt the user */
1349 fflush(stdout);
1350 fflush(stderr);
1352 vfprintf(stream, fmt, args);
1354 if(preset) {
1355 fprintf(stream, " %s ", _("[Y/n]"));
1356 } else {
1357 fprintf(stream, " %s ", _("[y/N]"));
1360 if(config->noconfirm) {
1361 fprintf(stream, "\n");
1362 return preset;
1365 fflush(stream);
1366 flush_term_input();
1368 if(fgets(response, sizeof(response), stdin)) {
1369 strtrim(response);
1370 if(strlen(response) == 0) {
1371 return preset;
1374 if(strcasecmp(response, _("Y")) == 0 || strcasecmp(response, _("YES")) == 0) {
1375 return 1;
1376 } else if(strcasecmp(response, _("N")) == 0 || strcasecmp(response, _("NO")) == 0) {
1377 return 0;
1380 return 0;
1383 int yesno(char *fmt, ...)
1385 int ret;
1386 va_list args;
1388 va_start(args, fmt);
1389 ret = question(1, fmt, args);
1390 va_end(args);
1392 return ret;
1395 int noyes(char *fmt, ...)
1397 int ret;
1398 va_list args;
1400 va_start(args, fmt);
1401 ret = question(0, fmt, args);
1402 va_end(args);
1404 return ret;
1407 int pm_printf(alpm_loglevel_t level, const char *format, ...)
1409 int ret;
1410 va_list args;
1412 /* print the message using va_arg list */
1413 va_start(args, format);
1414 ret = pm_vfprintf(stdout, level, format, args);
1415 va_end(args);
1417 return ret;
1420 int pm_fprintf(FILE *stream, alpm_loglevel_t level, const char *format, ...)
1422 int ret;
1423 va_list args;
1425 /* print the message using va_arg list */
1426 va_start(args, format);
1427 ret = pm_vfprintf(stream, level, format, args);
1428 va_end(args);
1430 return ret;
1433 int pm_asprintf(char **string, const char *format, ...)
1435 int ret = 0;
1436 va_list args;
1438 /* print the message using va_arg list */
1439 va_start(args, format);
1440 if(vasprintf(string, format, args) == -1) {
1441 pm_fprintf(stderr, ALPM_LOG_ERROR, _("failed to allocate string\n"));
1442 ret = -1;
1444 va_end(args);
1446 return ret;
1449 int pm_vasprintf(char **string, alpm_loglevel_t level, const char *format, va_list args)
1451 int ret = 0;
1452 char *msg = NULL;
1454 /* if current logmask does not overlap with level, do not print msg */
1455 if(!(config->logmask & level)) {
1456 return ret;
1459 /* print the message using va_arg list */
1460 ret = vasprintf(&msg, format, args);
1462 /* print a prefix to the message */
1463 switch(level) {
1464 case ALPM_LOG_ERROR:
1465 pm_asprintf(string, _("error: %s"), msg);
1466 break;
1467 case ALPM_LOG_WARNING:
1468 pm_asprintf(string, _("warning: %s"), msg);
1469 break;
1470 case ALPM_LOG_DEBUG:
1471 pm_asprintf(string, "debug: %s", msg);
1472 break;
1473 case ALPM_LOG_FUNCTION:
1474 pm_asprintf(string, "function: %s", msg);
1475 break;
1476 default:
1477 pm_asprintf(string, "%s", msg);
1478 break;
1480 free(msg);
1482 return ret;
1485 int pm_vfprintf(FILE *stream, alpm_loglevel_t level, const char *format, va_list args)
1487 int ret = 0;
1489 /* if current logmask does not overlap with level, do not print msg */
1490 if(!(config->logmask & level)) {
1491 return ret;
1494 #if defined(PACMAN_DEBUG)
1495 /* If debug is on, we'll timestamp the output */
1496 if(config->logmask & ALPM_LOG_DEBUG) {
1497 time_t t;
1498 struct tm *tmp;
1499 char timestr[10] = {0};
1501 t = time(NULL);
1502 tmp = localtime(&t);
1503 strftime(timestr, 9, "%H:%M:%S", tmp);
1504 timestr[8] = '\0';
1506 fprintf(stream, "[%s] ", timestr);
1508 #endif
1510 /* print a prefix to the message */
1511 switch(level) {
1512 case ALPM_LOG_ERROR:
1513 fprintf(stream, _("error: "));
1514 break;
1515 case ALPM_LOG_WARNING:
1516 fprintf(stream, _("warning: "));
1517 break;
1518 case ALPM_LOG_DEBUG:
1519 fprintf(stream, "debug: ");
1520 break;
1521 case ALPM_LOG_FUNCTION:
1522 fprintf(stream, "function: ");
1523 break;
1524 default:
1525 break;
1528 /* print the message using va_arg list */
1529 ret = vfprintf(stream, format, args);
1530 return ret;
1533 #ifndef HAVE_STRNDUP
1534 /* A quick and dirty implementation derived from glibc */
1535 static size_t strnlen(const char *s, size_t max)
1537 register const char *p;
1538 for(p = s; *p && max--; ++p);
1539 return (p - s);
1542 char *strndup(const char *s, size_t n)
1544 size_t len = strnlen(s, n);
1545 char *new = (char *) malloc(len + 1);
1547 if(new == NULL)
1548 return NULL;
1550 new[len] = '\0';
1551 return (char *)memcpy(new, s, len);
1553 #endif
1555 /* vim: set ts=2 sw=2 noet: */