Show net upgrade size on -U/-S operations
[pacman-ng.git] / src / pacman / util.c
blobbfc707ca2c00de5ab5172fcd2e3fe4829d009c4f
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(pmtransflag_t flags)
54 int ret;
55 if(config->print) {
56 ret = alpm_trans_init(flags, NULL, NULL, NULL);
57 } else {
58 ret = alpm_trans_init(flags, cb_trans_evt, cb_trans_conv,
59 cb_trans_progress);
62 if(ret == -1) {
63 pm_fprintf(stderr, PM_LOG_ERROR, _("failed to init transaction (%s)\n"),
64 alpm_strerrorlast());
65 if(pm_errno == PM_ERR_HANDLE_LOCK) {
66 fprintf(stderr, _(" if you're sure a package manager is not already\n"
67 " running, you can remove %s\n"), alpm_option_get_lockfile());
69 else if(pm_errno == PM_ERR_DB_VERSION) {
70 fprintf(stderr, _(" try running pacman-db-upgrade\n"));
73 return -1;
75 return 0;
78 int trans_release(void)
80 if(alpm_trans_release() == -1) {
81 pm_fprintf(stderr, PM_LOG_ERROR, _("failed to release transaction (%s)\n"),
82 alpm_strerrorlast());
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 /* discard unhandled input on the terminal's input buffer */
106 static int flush_term_input(void) {
107 #ifdef HAVE_TCFLUSH
108 if(isatty(fileno(stdin))) {
109 return(tcflush(fileno(stdin), TCIFLUSH));
111 #endif
113 /* fail silently */
114 return 0;
117 /* gets the current screen column width */
118 int getcols(void)
120 #ifdef TIOCGSIZE
121 struct ttysize win;
122 if(ioctl(1, TIOCGSIZE, &win) == 0) {
123 return win.ts_cols;
125 #elif defined(TIOCGWINSZ)
126 struct winsize win;
127 if(ioctl(1, TIOCGWINSZ, &win) == 0) {
128 return win.ws_col;
130 #endif
131 return 0;
134 /* does the same thing as 'rm -rf' */
135 int rmrf(const char *path)
137 int errflag = 0;
138 struct dirent *dp;
139 DIR *dirp;
141 if(!unlink(path)) {
142 return 0;
143 } else {
144 if(errno == ENOENT) {
145 return 0;
146 } else if(errno == EPERM) {
147 /* fallthrough */
148 } else if(errno == EISDIR) {
149 /* fallthrough */
150 } else if(errno == ENOTDIR) {
151 return 1;
152 } else {
153 /* not a directory */
154 return 1;
157 dirp = opendir(path);
158 if(!dirp) {
159 return 1;
161 for(dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
162 if(dp->d_ino) {
163 char name[PATH_MAX];
164 sprintf(name, "%s/%s", path, dp->d_name);
165 if(strcmp(dp->d_name, "..") != 0 && strcmp(dp->d_name, ".") != 0) {
166 errflag += rmrf(name);
170 closedir(dirp);
171 if(rmdir(path)) {
172 errflag++;
174 return errflag;
178 /** Parse the basename of a program from a path.
179 * @param path path to parse basename from
181 * @return everything following the final '/'
183 const char *mbasename(const char *path)
185 const char *last = strrchr(path, '/');
186 if(last) {
187 return last + 1;
189 return path;
192 /** Parse the dirname of a program from a path.
193 * The path returned should be freed.
194 * @param path path to parse dirname from
196 * @return everything preceding the final '/'
198 char *mdirname(const char *path)
200 char *ret, *last;
202 /* null or empty path */
203 if(path == NULL || path == '\0') {
204 return strdup(".");
207 ret = strdup(path);
208 last = strrchr(ret, '/');
210 if(last != NULL) {
211 /* we found a '/', so terminate our string */
212 *last = '\0';
213 return ret;
215 /* no slash found */
216 free(ret);
217 return strdup(".");
220 /* output a string, but wrap words properly with a specified indentation
222 void indentprint(const char *str, int indent)
224 wchar_t *wcstr;
225 const wchar_t *p;
226 int len, cidx, cols;
228 if(!str) {
229 return;
232 cols = getcols();
234 /* if we're not a tty, print without indenting */
235 if(cols == 0) {
236 printf("%s", str);
237 return;
240 len = strlen(str) + 1;
241 wcstr = calloc(len, sizeof(wchar_t));
242 len = mbstowcs(wcstr, str, len);
243 p = wcstr;
244 cidx = indent;
246 if(!p || !len) {
247 return;
250 while(*p) {
251 if(*p == L' ') {
252 const wchar_t *q, *next;
253 p++;
254 if(p == NULL || *p == L' ') continue;
255 next = wcschr(p, L' ');
256 if(next == NULL) {
257 next = p + wcslen(p);
259 /* len captures # cols */
260 len = 0;
261 q = p;
262 while(q < next) {
263 len += wcwidth(*q++);
265 if(len > (cols - cidx - 1)) {
266 /* wrap to a newline and reindent */
267 printf("\n%-*s", indent, "");
268 cidx = indent;
269 } else {
270 printf(" ");
271 cidx++;
273 continue;
275 printf("%lc", (wint_t)*p);
276 cidx += wcwidth(*p);
277 p++;
279 free(wcstr);
282 /* Convert a string to uppercase
284 char *strtoupper(char *str)
286 char *ptr = str;
288 while(*ptr) {
289 (*ptr) = (char)toupper((unsigned char)*ptr);
290 ptr++;
292 return str;
295 /* Trim whitespace and newlines from a string
297 char *strtrim(char *str)
299 char *pch = str;
301 if(str == NULL || *str == '\0') {
302 /* string is empty, so we're done. */
303 return str;
306 while(isspace((unsigned char)*pch)) {
307 pch++;
309 if(pch != str) {
310 memmove(str, pch, (strlen(pch) + 1));
313 /* check if there wasn't anything but whitespace in the string. */
314 if(*str == '\0') {
315 return str;
318 pch = (str + (strlen(str) - 1));
319 while(isspace((unsigned char)*pch)) {
320 pch--;
322 *++pch = '\0';
324 return str;
327 /* Replace all occurances of 'needle' with 'replace' in 'str', returning
328 * a new string (must be free'd) */
329 char *strreplace(const char *str, const char *needle, const char *replace)
331 const char *p = NULL, *q = NULL;
332 char *newstr = NULL, *newp = NULL;
333 alpm_list_t *i = NULL, *list = NULL;
334 size_t needlesz = strlen(needle), replacesz = strlen(replace);
335 size_t newsz;
337 if(!str) {
338 return NULL;
341 p = str;
342 q = strstr(p, needle);
343 while(q) {
344 list = alpm_list_add(list, (char *)q);
345 p = q + needlesz;
346 q = strstr(p, needle);
349 /* no occurences of needle found */
350 if(!list) {
351 return strdup(str);
353 /* size of new string = size of old string + "number of occurences of needle"
354 * x "size difference between replace and needle" */
355 newsz = strlen(str) + 1 +
356 alpm_list_count(list) * (replacesz - needlesz);
357 newstr = malloc(newsz);
358 if(!newstr) {
359 return NULL;
361 *newstr = '\0';
363 p = str;
364 newp = newstr;
365 for(i = list; i; i = alpm_list_next(i)) {
366 q = alpm_list_getdata(i);
367 if(q > p){
368 /* add chars between this occurence and last occurence, if any */
369 strncpy(newp, p, (size_t)(q - p));
370 newp += q - p;
372 strncpy(newp, replace, replacesz);
373 newp += replacesz;
374 p = q + needlesz;
376 alpm_list_free(list);
378 if(*p) {
379 /* add the rest of 'p' */
380 strcpy(newp, p);
381 newp += strlen(p);
383 *newp = '\0';
385 return newstr;
388 /** Splits a string into a list of strings using the chosen character as
389 * a delimiter.
391 * @param str the string to split
392 * @param splitchar the character to split at
394 * @return a list containing the duplicated strings
396 alpm_list_t *strsplit(const char *str, const char splitchar)
398 alpm_list_t *list = NULL;
399 const char *prev = str;
400 char *dup = NULL;
402 while((str = strchr(str, splitchar))) {
403 dup = strndup(prev, (size_t)(str - prev));
404 if(dup == NULL) {
405 return NULL;
407 list = alpm_list_add(list, dup);
409 str++;
410 prev = str;
413 dup = strdup(prev);
414 if(dup == NULL) {
415 return NULL;
417 list = alpm_list_add(list, dup);
419 return list;
422 static int string_length(const char *s)
424 int len;
425 wchar_t *wcstr;
427 if(!s) {
428 return 0;
430 /* len goes from # bytes -> # chars -> # cols */
431 len = strlen(s) + 1;
432 wcstr = calloc(len, sizeof(wchar_t));
433 len = mbstowcs(wcstr, s, len);
434 len = wcswidth(wcstr, len);
435 free(wcstr);
437 return len;
440 void string_display(const char *title, const char *string)
442 int len = 0;
444 if(title) {
445 printf("%s ", title);
447 if(string == NULL || string[0] == '\0') {
448 printf(_("None"));
449 } else {
450 /* compute the length of title + a space */
451 len = string_length(title) + 1;
452 indentprint(string, len);
454 printf("\n");
457 static void table_print_line(const alpm_list_t *line,
458 const alpm_list_t *formats)
460 const alpm_list_t *curformat = formats;
461 const alpm_list_t *curcell = line;
463 while(curcell && curformat) {
464 printf(alpm_list_getdata(curformat), alpm_list_getdata(curcell));
465 curcell = alpm_list_next(curcell);
466 curformat = alpm_list_next(curformat);
469 printf("\n");
472 /* creates format strings by checking max cell lengths in cols */
473 static alpm_list_t *table_create_format(const alpm_list_t *header,
474 const alpm_list_t *rows)
476 alpm_list_t *longest_str, *longest_strs = NULL;
477 alpm_list_t *formats = NULL;
478 const alpm_list_t *i, *row, *cell;
479 char *str, *formatstr;
480 const int padding = 2;
481 int colwidth, totalwidth = 0;
482 int curcol = 0;
484 /* header determines column count and initial values of longest_strs */
485 for(i = header; i; i = alpm_list_next(i)) {
486 longest_strs = alpm_list_add(longest_strs, alpm_list_getdata(i));
489 /* now find the longest string in each column */
490 for(longest_str = longest_strs; longest_str;
491 longest_str = alpm_list_next(longest_str), curcol++) {
492 for(i = rows; i; i = alpm_list_next(i)) {
493 row = alpm_list_getdata(i);
494 cell = alpm_list_nth(row, curcol);
495 str = alpm_list_getdata(cell);
497 if(strlen(str) > strlen(alpm_list_getdata(longest_str))) {
498 longest_str->data = str;
503 /* now use the column width info to generate format strings */
504 for(i = longest_strs; i; i = alpm_list_next(i)) {
505 const char *display;
506 colwidth = strlen(alpm_list_getdata(i)) + padding;
507 totalwidth += colwidth;
509 /* right align the last column for a cleaner table display */
510 display = (alpm_list_next(i) != NULL) ? "%%-%ds" : "%%%ds";
511 pm_asprintf(&formatstr, display, colwidth);
513 formats = alpm_list_add(formats, formatstr);
516 alpm_list_free(longest_strs);
518 /* return NULL if terminal is not wide enough */
519 if(totalwidth > getcols()) {
520 fprintf(stderr, _("insufficient columns available for table display\n"));
521 FREELIST(formats);
522 return(NULL);
525 return(formats);
528 /** Displays the list in table format
530 * @param title the tables title
531 * @param header the column headers. column count is determined by the nr
532 * of headers
533 * @param rows the rows to display as a list of lists of strings. the outer
534 * list represents the rows, the inner list the cells (= columns)
536 * @return -1 if not enough terminal cols available, else 0
538 int table_display(const char *title, const alpm_list_t *header,
539 const alpm_list_t *rows)
541 const alpm_list_t *i;
542 alpm_list_t *formats;
544 if(rows == NULL || header == NULL) {
545 return(0);
548 formats = table_create_format(header, rows);
549 if(formats == NULL) {
550 return(-1);
553 if(title != NULL) {
554 printf("%s\n\n", title);
557 table_print_line(header, formats);
558 printf("\n");
560 for(i = rows; i; i = alpm_list_next(i)) {
561 table_print_line(alpm_list_getdata(i), formats);
564 FREELIST(formats);
565 return(0);
568 void list_display(const char *title, const alpm_list_t *list)
570 const alpm_list_t *i;
571 int cols, len = 0;
573 if(title) {
574 len = string_length(title) + 1;
575 printf("%s ", title);
578 if(!list) {
579 printf("%s\n", _("None"));
580 } else {
581 for(i = list, cols = len; i; i = alpm_list_next(i)) {
582 char *str = alpm_list_getdata(i);
583 int s = string_length(str);
584 int maxcols = getcols();
585 if(maxcols > 0 && (cols + s + 2) >= maxcols) {
586 int j;
587 cols = len;
588 printf("\n");
589 for (j = 1; j <= len; j++) {
590 printf(" ");
592 } else if(cols != len) {
593 /* 2 spaces are added if this is not the first element on a line. */
594 printf(" ");
595 cols += 2;
597 printf("%s", str);
598 cols += s;
600 printf("\n");
604 void list_display_linebreak(const char *title, const alpm_list_t *list)
606 const alpm_list_t *i;
607 int len = 0;
609 if(title) {
610 len = string_length(title) + 1;
611 printf("%s ", title);
614 if(!list) {
615 printf("%s\n", _("None"));
616 } else {
617 /* Print the first element */
618 indentprint((const char *) alpm_list_getdata(list), len);
619 printf("\n");
620 /* Print the rest */
621 for(i = alpm_list_next(list); i; i = alpm_list_next(i)) {
622 int j;
623 for(j = 1; j <= len; j++) {
624 printf(" ");
626 indentprint((const char *) alpm_list_getdata(i), len);
627 printf("\n");
632 /* creates a header row for use with table_display */
633 static alpm_list_t *create_verbose_header(int install)
635 alpm_list_t *res = NULL;
636 char *str;
638 pm_asprintf(&str, "%s", _("Name"));
639 res = alpm_list_add(res, str);
640 pm_asprintf(&str, "%s", _("Old Version"));
641 res = alpm_list_add(res, str);
642 if(install) {
643 pm_asprintf(&str, "%s", _("New Version"));
644 res = alpm_list_add(res, str);
646 pm_asprintf(&str, "%s", _("Size"));
647 res = alpm_list_add(res, str);
649 return(res);
652 /* returns package info as list of strings */
653 static alpm_list_t *create_verbose_row(pmpkg_t *pkg, int install)
655 char *str;
656 double size;
657 const char *label;
658 alpm_list_t *ret = NULL;
659 pmdb_t *ldb = alpm_option_get_localdb();
661 /* a row consists of the package name, */
662 pm_asprintf(&str, "%s", alpm_pkg_get_name(pkg));
663 ret = alpm_list_add(ret, str);
665 /* old and new versions */
666 if(install) {
667 pmpkg_t *oldpkg = alpm_db_get_pkg(ldb, alpm_pkg_get_name(pkg));
668 pm_asprintf(&str, "%s",
669 oldpkg != NULL ? alpm_pkg_get_version(oldpkg) : "");
670 ret = alpm_list_add(ret, str);
673 pm_asprintf(&str, "%s", alpm_pkg_get_version(pkg));
674 ret = alpm_list_add(ret, str);
676 /* and size */
677 size = humanize_size(alpm_pkg_get_size(pkg), 'M', 1, &label);
678 pm_asprintf(&str, "%.2f %s", size, label);
679 ret = alpm_list_add(ret, str);
681 return(ret);
684 /* prepare a list of pkgs to display */
685 void display_targets(const alpm_list_t *pkgs, int install)
687 char *str;
688 const char *title, *label;
689 double size;
690 const alpm_list_t *i;
691 off_t isize = 0, rsize = 0, dlsize = 0;
692 alpm_list_t *j, *lp, *header = NULL, *targets = NULL;
693 pmdb_t *db_local = alpm_option_get_localdb();
695 if(!pkgs) {
696 return;
699 /* gather pkg infos */
700 for(i = pkgs; i; i = alpm_list_next(i)) {
701 pmpkg_t *pkg = alpm_list_getdata(i);
703 if(install) {
704 pmpkg_t *lpkg = alpm_db_get_pkg(db_local, alpm_pkg_get_name(pkg));
705 dlsize += alpm_pkg_download_size(pkg);
706 if(lpkg) {
707 /* add up size of all removed packages */
708 rsize += alpm_pkg_get_isize(lpkg);
711 isize += alpm_pkg_get_isize(pkg);
713 if(config->verbosepkglists) {
714 targets = alpm_list_add(targets, create_verbose_row(pkg, install));
715 } else {
716 pm_asprintf(&str, "%s-%s", alpm_pkg_get_name(pkg),
717 alpm_pkg_get_version(pkg));
718 targets = alpm_list_add(targets, str);
722 /* print to screen */
723 title = install ? _("Targets (%d):") : _("Remove (%d):");
724 pm_asprintf(&str, title, alpm_list_count(pkgs));
726 printf("\n");
727 if(config->verbosepkglists) {
728 header = create_verbose_header(install);
729 if(table_display(str, header, targets) != 0) {
730 config->verbosepkglists = 0;
731 display_targets(pkgs, install);
732 goto out;
734 } else {
735 list_display(str, targets);
737 printf("\n");
739 if(install) {
740 size = humanize_size(dlsize, 'M', 1, &label);
741 printf(_("Total Download Size: %.2f %s\n"), size, label);
742 if(!(config->flags & PM_TRANS_FLAG_DOWNLOADONLY)) {
743 size = humanize_size(isize, 'M', 1, &label);
744 printf(_("Total Installed Size: %.2f %s\n"), size, label);
745 /* only show this net value if different from raw installed size */
746 if(rsize > 0) {
747 size = humanize_size(isize - rsize, 'M', 1, &label);
748 printf(_("Net Upgrade Size: %.2f %s\n"), size, label);
751 } else {
752 size = humanize_size(isize, 'M', 1, &label);
753 printf(_("Total Removed Size: %.2f %s\n"), size, label);
756 out:
757 /* cleanup */
758 if(config->verbosepkglists) {
759 /* targets is a list of lists of strings, free inner lists here */
760 for(j = alpm_list_first(targets); j; j = alpm_list_next(j)) {
761 lp = alpm_list_getdata(j);
762 FREELIST(lp);
764 alpm_list_free(targets);
765 FREELIST(header);
766 } else {
767 FREELIST(targets);
769 free(str);
772 static off_t pkg_get_size(pmpkg_t *pkg)
774 switch(config->op) {
775 case PM_OP_SYNC:
776 return alpm_pkg_download_size(pkg);
777 case PM_OP_UPGRADE:
778 return alpm_pkg_get_size(pkg);
779 default:
780 return alpm_pkg_get_isize(pkg);
784 static char *pkg_get_location(pmpkg_t *pkg)
786 pmdb_t *db;
787 const char *dburl;
788 char *string;
789 switch(config->op) {
790 case PM_OP_SYNC:
791 db = alpm_pkg_get_db(pkg);
792 dburl = alpm_db_get_url(db);
793 if(dburl) {
794 char *pkgurl = NULL;
795 pm_asprintf(&pkgurl, "%s/%s", dburl, alpm_pkg_get_filename(pkg));
796 return pkgurl;
798 case PM_OP_UPGRADE:
799 return strdup(alpm_pkg_get_filename(pkg));
800 default:
801 string = NULL;
802 pm_asprintf(&string, "%s-%s", alpm_pkg_get_name(pkg), alpm_pkg_get_version(pkg));
803 return string;
807 /** Converts sizes in bytes into human readable units.
809 * @param bytes the size in bytes
810 * @param target_unit '\0' or a short label. If equal to one of the short unit
811 * labels ('B', 'K', ...) bytes is converted to target_unit; if '\0', the first
812 * unit which will bring the value to below a threshold of 2048 will be chosen.
813 * @param long_labels whether to use short ("K") or long ("KiB") unit labels
814 * @param label will be set to the appropriate unit label
816 * @return the size in the appropriate unit
818 double humanize_size(off_t bytes, const char target_unit, int long_labels,
819 const char **label)
821 static const char *shortlabels[] = {"B", "K", "M", "G", "T", "P"};
822 static const char *longlabels[] = {"B", "KiB", "MiB", "GiB", "TiB", "PiB"};
823 static const int unitcount = sizeof(shortlabels) / sizeof(shortlabels[0]);
825 const char **labels = long_labels ? longlabels : shortlabels;
826 double val = (double)bytes;
827 int index;
829 for(index = 0; index < unitcount - 1; index++) {
830 if(target_unit != '\0' && shortlabels[index][0] == target_unit) {
831 break;
832 } else if(target_unit == '\0' && val <= 2048.0) {
833 break;
835 val /= 1024.0;
838 if(label) {
839 *label = labels[index];
842 return(val);
845 void print_packages(const alpm_list_t *packages)
847 const alpm_list_t *i;
848 if(!config->print_format) {
849 config->print_format = strdup("%l");
851 for(i = packages; i; i = alpm_list_next(i)) {
852 pmpkg_t *pkg = alpm_list_getdata(i);
853 char *string = strdup(config->print_format);
854 char *temp = string;
855 /* %n : pkgname */
856 if(strstr(temp,"%n")) {
857 string = strreplace(temp, "%n", alpm_pkg_get_name(pkg));
858 free(temp);
859 temp = string;
861 /* %v : pkgver */
862 if(strstr(temp,"%v")) {
863 string = strreplace(temp, "%v", alpm_pkg_get_version(pkg));
864 free(temp);
865 temp = string;
867 /* %l : location */
868 if(strstr(temp,"%l")) {
869 char *pkgloc = pkg_get_location(pkg);
870 string = strreplace(temp, "%l", pkgloc);
871 free(pkgloc);
872 free(temp);
873 temp = string;
875 /* %r : repo */
876 if(strstr(temp,"%r")) {
877 const char *repo = "local";
878 pmdb_t *db = alpm_pkg_get_db(pkg);
879 if(db) {
880 repo = alpm_db_get_name(db);
882 string = strreplace(temp, "%r", repo);
883 free(temp);
884 temp = string;
886 /* %s : size */
887 if(strstr(temp,"%s")) {
888 char *size;
889 pm_asprintf(&size, "%jd", (intmax_t)pkg_get_size(pkg));
890 string = strreplace(temp, "%s", size);
891 free(size);
892 free(temp);
894 printf("%s\n",string);
895 free(string);
899 /* Helper function for comparing strings using the
900 * alpm "compare func" signature */
901 int str_cmp(const void *s1, const void *s2)
903 return strcmp(s1, s2);
906 void display_new_optdepends(pmpkg_t *oldpkg, pmpkg_t *newpkg)
908 alpm_list_t *old = alpm_pkg_get_optdepends(oldpkg);
909 alpm_list_t *new = alpm_pkg_get_optdepends(newpkg);
910 alpm_list_t *optdeps = alpm_list_diff(new,old,str_cmp);
911 if(optdeps) {
912 printf(_("New optional dependencies for %s\n"), alpm_pkg_get_name(newpkg));
913 list_display_linebreak(" ", optdeps);
915 alpm_list_free(optdeps);
918 void display_optdepends(pmpkg_t *pkg)
920 alpm_list_t *optdeps = alpm_pkg_get_optdepends(pkg);
921 if(optdeps) {
922 printf(_("Optional dependencies for %s\n"), alpm_pkg_get_name(pkg));
923 list_display_linebreak(" ", optdeps);
927 static void display_repo_list(const char *dbname, alpm_list_t *list)
929 const char *prefix= " ";
931 printf(":: ");
932 printf(_("Repository %s\n"), dbname);
933 list_display(prefix, list);
936 void select_display(const alpm_list_t *pkglist)
938 const alpm_list_t *i;
939 int nth = 1;
940 alpm_list_t *list = NULL;
941 char *string = NULL;
942 const char *dbname = NULL;
944 for (i = pkglist; i; i = i->next) {
945 pmpkg_t *pkg = alpm_list_getdata(i);
946 pmdb_t *db = alpm_pkg_get_db(pkg);
948 if(!dbname)
949 dbname = alpm_db_get_name(db);
950 if(strcmp(alpm_db_get_name(db), dbname) != 0) {
951 display_repo_list(dbname, list);
952 FREELIST(list);
953 dbname = alpm_db_get_name(db);
955 string = NULL;
956 pm_asprintf(&string, "%d) %s", nth, alpm_pkg_get_name(pkg));
957 list = alpm_list_add(list, string);
958 nth++;
960 display_repo_list(dbname, list);
961 FREELIST(list);
964 static int parseindex(char *s, int *val, int min, int max)
966 char *endptr = NULL;
967 int n = strtol(s, &endptr, 10);
968 if(*endptr == '\0') {
969 if(n < min || n > max) {
970 fprintf(stderr, _("Invalid value: %d is not between %d and %d\n"),
971 n, min, max);
972 return -1;
974 *val = n;
975 return 0;
976 } else {
977 fprintf(stderr, _("Invalid number: %s\n"), s);
978 return -1;
982 static int multiselect_parse(char *array, int count, char *response)
984 char *str, *saveptr;
986 for (str = response; ; str = NULL) {
987 int include = 1;
988 int start, end;
989 char *ends = NULL;
990 char *starts = strtok_r(str, " ", &saveptr);
992 if(starts == NULL) {
993 break;
995 strtrim(starts);
996 int len = strlen(starts);
997 if(len == 0)
998 continue;
1000 if(*starts == '^') {
1001 starts++;
1002 len--;
1003 include = 0;
1004 } else if(str) {
1005 /* if first token is including, we unselect all targets */
1006 memset(array, 0, count);
1009 if(len > 1) {
1010 /* check for range */
1011 char *p;
1012 if((p = strchr(starts + 1, '-'))) {
1013 *p = 0;
1014 ends = p + 1;
1018 if(parseindex(starts, &start, 1, count) != 0)
1019 return -1;
1021 if(!ends) {
1022 array[start-1] = include;
1023 } else {
1024 int d;
1025 if(parseindex(ends, &end, start, count) != 0) {
1026 return -1;
1028 for(d = start; d <= end; d++) {
1029 array[d-1] = include;
1034 return 0;
1037 int multiselect_question(char *array, int count)
1039 char response[64];
1040 FILE *stream;
1042 if(config->noconfirm) {
1043 stream = stdout;
1044 } else {
1045 /* Use stderr so questions are always displayed when redirecting output */
1046 stream = stderr;
1049 while(1) {
1050 memset(array, 1, count);
1052 fprintf(stream, "\n");
1053 fprintf(stream, _("Enter a selection (default=all)"));
1054 fprintf(stream, ": ");
1056 if(config->noconfirm) {
1057 fprintf(stream, "\n");
1058 break;
1061 flush_term_input();
1063 if(fgets(response, sizeof(response), stdin)) {
1064 strtrim(response);
1065 if(strlen(response) > 0) {
1066 if(multiselect_parse(array, count, response) == -1) {
1067 /* only loop if user gave an invalid answer */
1068 continue;
1072 break;
1074 return 0;
1077 int select_question(int count)
1079 char response[32];
1080 FILE *stream;
1081 int preset = 1;
1083 if(config->noconfirm) {
1084 stream = stdout;
1085 } else {
1086 /* Use stderr so questions are always displayed when redirecting output */
1087 stream = stderr;
1090 while(1) {
1091 fprintf(stream, "\n");
1092 fprintf(stream, _("Enter a number (default=%d)"), preset);
1093 fprintf(stream, ": ");
1095 if(config->noconfirm) {
1096 fprintf(stream, "\n");
1097 break;
1100 flush_term_input();
1102 if(fgets(response, sizeof(response), stdin)) {
1103 strtrim(response);
1104 if(strlen(response) > 0) {
1105 int n;
1106 if(parseindex(response, &n, 1, count) != 0)
1107 continue;
1108 return (n - 1);
1111 break;
1114 return (preset - 1);
1118 /* presents a prompt and gets a Y/N answer */
1119 static int question(short preset, char *fmt, va_list args)
1121 char response[32];
1122 FILE *stream;
1124 if(config->noconfirm) {
1125 stream = stdout;
1126 } else {
1127 /* Use stderr so questions are always displayed when redirecting output */
1128 stream = stderr;
1131 /* ensure all text makes it to the screen before we prompt the user */
1132 fflush(stdout);
1133 fflush(stderr);
1135 vfprintf(stream, fmt, args);
1137 if(preset) {
1138 fprintf(stream, " %s ", _("[Y/n]"));
1139 } else {
1140 fprintf(stream, " %s ", _("[y/N]"));
1143 if(config->noconfirm) {
1144 fprintf(stream, "\n");
1145 return preset;
1148 fflush(stream);
1149 flush_term_input();
1151 if(fgets(response, sizeof(response), stdin)) {
1152 strtrim(response);
1153 if(strlen(response) == 0) {
1154 return preset;
1157 if(strcasecmp(response, _("Y")) == 0 || strcasecmp(response, _("YES")) == 0) {
1158 return 1;
1159 } else if(strcasecmp(response, _("N")) == 0 || strcasecmp(response, _("NO")) == 0) {
1160 return 0;
1163 return 0;
1166 int yesno(char *fmt, ...)
1168 int ret;
1169 va_list args;
1171 va_start(args, fmt);
1172 ret = question(1, fmt, args);
1173 va_end(args);
1175 return ret;
1178 int noyes(char *fmt, ...)
1180 int ret;
1181 va_list args;
1183 va_start(args, fmt);
1184 ret = question(0, fmt, args);
1185 va_end(args);
1187 return ret;
1190 int pm_printf(pmloglevel_t level, const char *format, ...)
1192 int ret;
1193 va_list args;
1195 /* print the message using va_arg list */
1196 va_start(args, format);
1197 ret = pm_vfprintf(stdout, level, format, args);
1198 va_end(args);
1200 return ret;
1203 int pm_fprintf(FILE *stream, pmloglevel_t level, const char *format, ...)
1205 int ret;
1206 va_list args;
1208 /* print the message using va_arg list */
1209 va_start(args, format);
1210 ret = pm_vfprintf(stream, level, format, args);
1211 va_end(args);
1213 return ret;
1216 int pm_asprintf(char **string, const char *format, ...)
1218 int ret = 0;
1219 va_list args;
1221 /* print the message using va_arg list */
1222 va_start(args, format);
1223 if(vasprintf(string, format, args) == -1) {
1224 pm_fprintf(stderr, PM_LOG_ERROR, _("failed to allocate string\n"));
1225 ret = -1;
1227 va_end(args);
1229 return ret;
1232 int pm_vasprintf(char **string, pmloglevel_t level, const char *format, va_list args)
1234 int ret = 0;
1235 char *msg = NULL;
1237 /* if current logmask does not overlap with level, do not print msg */
1238 if(!(config->logmask & level)) {
1239 return ret;
1242 /* print the message using va_arg list */
1243 ret = vasprintf(&msg, format, args);
1245 /* print a prefix to the message */
1246 switch(level) {
1247 case PM_LOG_ERROR:
1248 pm_asprintf(string, _("error: %s"), msg);
1249 break;
1250 case PM_LOG_WARNING:
1251 pm_asprintf(string, _("warning: %s"), msg);
1252 break;
1253 case PM_LOG_DEBUG:
1254 pm_asprintf(string, "debug: %s", msg);
1255 break;
1256 case PM_LOG_FUNCTION:
1257 pm_asprintf(string, "function: %s", msg);
1258 break;
1259 default:
1260 pm_asprintf(string, "%s", msg);
1261 break;
1263 free(msg);
1265 return ret;
1268 int pm_vfprintf(FILE *stream, pmloglevel_t level, const char *format, va_list args)
1270 int ret = 0;
1272 /* if current logmask does not overlap with level, do not print msg */
1273 if(!(config->logmask & level)) {
1274 return ret;
1277 #if defined(PACMAN_DEBUG)
1278 /* If debug is on, we'll timestamp the output */
1279 if(config->logmask & PM_LOG_DEBUG) {
1280 time_t t;
1281 struct tm *tmp;
1282 char timestr[10] = {0};
1284 t = time(NULL);
1285 tmp = localtime(&t);
1286 strftime(timestr, 9, "%H:%M:%S", tmp);
1287 timestr[8] = '\0';
1289 printf("[%s] ", timestr);
1291 #endif
1293 /* print a prefix to the message */
1294 switch(level) {
1295 case PM_LOG_ERROR:
1296 fprintf(stream, _("error: "));
1297 break;
1298 case PM_LOG_WARNING:
1299 fprintf(stream, _("warning: "));
1300 break;
1301 case PM_LOG_DEBUG:
1302 fprintf(stream, "debug: ");
1303 break;
1304 case PM_LOG_FUNCTION:
1305 fprintf(stream, "function: ");
1306 break;
1307 default:
1308 break;
1311 /* print the message using va_arg list */
1312 ret = vfprintf(stream, format, args);
1313 return ret;
1316 #ifndef HAVE_STRNDUP
1317 /* A quick and dirty implementation derived from glibc */
1318 static size_t strnlen(const char *s, size_t max)
1320 register const char *p;
1321 for(p = s; *p && max--; ++p);
1322 return (p - s);
1325 char *strndup(const char *s, size_t n)
1327 size_t len = strnlen(s, n);
1328 char *new = (char *) malloc(len + 1);
1330 if(new == NULL)
1331 return NULL;
1333 new[len] = '\0';
1334 return (char *)memcpy(new, s, len);
1336 #endif
1338 /* vim: set ts=2 sw=2 noet: */