Require handle argument to all alpm_trans_*() methods
[pacman-ng.git] / src / pacman / util.c
blob043b9d6fcdb2a0f6f193844160ee159b88c707ef
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(config->handle, flags, NULL, NULL, NULL);
57 } else {
58 ret = alpm_trans_init(config->handle, 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"),
68 alpm_option_get_lockfile(config->handle));
70 else if(pm_errno == PM_ERR_DB_VERSION) {
71 fprintf(stderr, _(" try running pacman-db-upgrade\n"));
74 return -1;
76 return 0;
79 int trans_release(void)
81 if(alpm_trans_release(config->handle) == -1) {
82 pm_fprintf(stderr, PM_LOG_ERROR, _("failed to release transaction (%s)\n"),
83 alpm_strerrorlast());
84 return -1;
86 return 0;
89 int needs_root(void)
91 switch(config->op) {
92 case PM_OP_DATABASE:
93 return 1;
94 case PM_OP_UPGRADE:
95 case PM_OP_REMOVE:
96 return !config->print;
97 case PM_OP_SYNC:
98 return (config->op_s_clean || config->op_s_sync ||
99 (!config->group && !config->op_s_info && !config->op_q_list &&
100 !config->op_s_search && !config->print));
101 default:
102 return 0;
106 /* discard unhandled input on the terminal's input buffer */
107 static int flush_term_input(void) {
108 #ifdef HAVE_TCFLUSH
109 if(isatty(fileno(stdin))) {
110 return(tcflush(fileno(stdin), TCIFLUSH));
112 #endif
114 /* fail silently */
115 return 0;
118 /* gets the current screen column width */
119 int getcols(int def)
121 #ifdef TIOCGSIZE
122 struct ttysize win;
123 if(ioctl(1, TIOCGSIZE, &win) == 0) {
124 return win.ts_cols;
126 #elif defined(TIOCGWINSZ)
127 struct winsize win;
128 if(ioctl(1, TIOCGWINSZ, &win) == 0) {
129 return win.ws_col;
131 #endif
132 return def;
135 /* does the same thing as 'rm -rf' */
136 int rmrf(const char *path)
138 int errflag = 0;
139 struct dirent *dp;
140 DIR *dirp;
142 if(!unlink(path)) {
143 return 0;
144 } else {
145 if(errno == ENOENT) {
146 return 0;
147 } else if(errno == EPERM) {
148 /* fallthrough */
149 } else if(errno == EISDIR) {
150 /* fallthrough */
151 } else if(errno == ENOTDIR) {
152 return 1;
153 } else {
154 /* not a directory */
155 return 1;
158 dirp = opendir(path);
159 if(!dirp) {
160 return 1;
162 for(dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
163 if(dp->d_ino) {
164 char name[PATH_MAX];
165 sprintf(name, "%s/%s", path, dp->d_name);
166 if(strcmp(dp->d_name, "..") != 0 && strcmp(dp->d_name, ".") != 0) {
167 errflag += rmrf(name);
171 closedir(dirp);
172 if(rmdir(path)) {
173 errflag++;
175 return errflag;
179 /** Parse the basename of a program from a path.
180 * @param path path to parse basename from
182 * @return everything following the final '/'
184 const char *mbasename(const char *path)
186 const char *last = strrchr(path, '/');
187 if(last) {
188 return last + 1;
190 return path;
193 /** Parse the dirname of a program from a path.
194 * The path returned should be freed.
195 * @param path path to parse dirname from
197 * @return everything preceding the final '/'
199 char *mdirname(const char *path)
201 char *ret, *last;
203 /* null or empty path */
204 if(path == NULL || path == '\0') {
205 return strdup(".");
208 ret = strdup(path);
209 last = strrchr(ret, '/');
211 if(last != NULL) {
212 /* we found a '/', so terminate our string */
213 *last = '\0';
214 return ret;
216 /* no slash found */
217 free(ret);
218 return strdup(".");
221 /* output a string, but wrap words properly with a specified indentation
223 void indentprint(const char *str, int indent)
225 wchar_t *wcstr;
226 const wchar_t *p;
227 int len, cidx;
228 const int cols = getcols(0);
230 if(!str) {
231 return;
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 if(title) {
443 printf("%s ", title);
445 if(string == NULL || string[0] == '\0') {
446 printf(_("None"));
447 } else {
448 /* compute the length of title + a space */
449 int len = string_length(title) + 1;
450 indentprint(string, len);
452 printf("\n");
455 static void table_print_line(const alpm_list_t *line,
456 const alpm_list_t *formats)
458 const alpm_list_t *curformat = formats;
459 const alpm_list_t *curcell = line;
461 while(curcell && curformat) {
462 printf(alpm_list_getdata(curformat), alpm_list_getdata(curcell));
463 curcell = alpm_list_next(curcell);
464 curformat = alpm_list_next(curformat);
467 printf("\n");
470 /* creates format strings by checking max cell lengths in cols */
471 static alpm_list_t *table_create_format(const alpm_list_t *header,
472 const alpm_list_t *rows)
474 alpm_list_t *longest_str, *longest_strs = NULL;
475 alpm_list_t *formats = NULL;
476 const alpm_list_t *i, *row, *cell;
477 char *str, *formatstr;
478 const int padding = 2;
479 int colwidth, totalwidth = 0;
480 int curcol = 0;
482 /* header determines column count and initial values of longest_strs */
483 for(i = header; i; i = alpm_list_next(i)) {
484 longest_strs = alpm_list_add(longest_strs, alpm_list_getdata(i));
487 /* now find the longest string in each column */
488 for(longest_str = longest_strs; longest_str;
489 longest_str = alpm_list_next(longest_str), curcol++) {
490 for(i = rows; i; i = alpm_list_next(i)) {
491 row = alpm_list_getdata(i);
492 cell = alpm_list_nth(row, curcol);
493 str = alpm_list_getdata(cell);
495 if(strlen(str) > strlen(alpm_list_getdata(longest_str))) {
496 longest_str->data = str;
501 /* now use the column width info to generate format strings */
502 for(i = longest_strs; i; i = alpm_list_next(i)) {
503 const char *display;
504 colwidth = strlen(alpm_list_getdata(i)) + padding;
505 totalwidth += colwidth;
507 /* right align the last column for a cleaner table display */
508 display = (alpm_list_next(i) != NULL) ? "%%-%ds" : "%%%ds";
509 pm_asprintf(&formatstr, display, colwidth);
511 formats = alpm_list_add(formats, formatstr);
514 alpm_list_free(longest_strs);
516 /* return NULL if terminal is not wide enough */
517 if(totalwidth > getcols(80)) {
518 fprintf(stderr, _("insufficient columns available for table display\n"));
519 FREELIST(formats);
520 return(NULL);
523 return(formats);
526 /** Displays the list in table format
528 * @param title the tables title
529 * @param header the column headers. column count is determined by the nr
530 * of headers
531 * @param rows the rows to display as a list of lists of strings. the outer
532 * list represents the rows, the inner list the cells (= columns)
534 * @return -1 if not enough terminal cols available, else 0
536 int table_display(const char *title, const alpm_list_t *header,
537 const alpm_list_t *rows)
539 const alpm_list_t *i;
540 alpm_list_t *formats;
542 if(rows == NULL || header == NULL) {
543 return(0);
546 formats = table_create_format(header, rows);
547 if(formats == NULL) {
548 return(-1);
551 if(title != NULL) {
552 printf("%s\n\n", title);
555 table_print_line(header, formats);
556 printf("\n");
558 for(i = rows; i; i = alpm_list_next(i)) {
559 table_print_line(alpm_list_getdata(i), formats);
562 FREELIST(formats);
563 return(0);
566 void list_display(const char *title, const alpm_list_t *list)
568 const alpm_list_t *i;
569 int len = 0;
571 if(title) {
572 len = string_length(title) + 1;
573 printf("%s ", title);
576 if(!list) {
577 printf("%s\n", _("None"));
578 } else {
579 int cols;
580 const int maxcols = getcols(80);
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 if(cols + s + 2 >= maxcols) {
585 int j;
586 cols = len;
587 printf("\n");
588 for (j = 1; j <= len; j++) {
589 printf(" ");
591 } else if(cols != len) {
592 /* 2 spaces are added if this is not the first element on a line. */
593 printf(" ");
594 cols += 2;
596 printf("%s", str);
597 cols += s;
599 printf("\n");
603 void list_display_linebreak(const char *title, const alpm_list_t *list)
605 const alpm_list_t *i;
606 int len = 0;
608 if(title) {
609 len = string_length(title) + 1;
610 printf("%s ", title);
613 if(!list) {
614 printf("%s\n", _("None"));
615 } else {
616 /* Print the first element */
617 indentprint((const char *) alpm_list_getdata(list), len);
618 printf("\n");
619 /* Print the rest */
620 for(i = alpm_list_next(list); i; i = alpm_list_next(i)) {
621 int j;
622 for(j = 1; j <= len; j++) {
623 printf(" ");
625 indentprint((const char *) alpm_list_getdata(i), len);
626 printf("\n");
631 /* creates a header row for use with table_display */
632 static alpm_list_t *create_verbose_header(int install)
634 alpm_list_t *res = NULL;
635 char *str;
637 pm_asprintf(&str, "%s", _("Name"));
638 res = alpm_list_add(res, str);
639 pm_asprintf(&str, "%s", _("Old Version"));
640 res = alpm_list_add(res, str);
641 if(install) {
642 pm_asprintf(&str, "%s", _("New Version"));
643 res = alpm_list_add(res, str);
645 pm_asprintf(&str, "%s", _("Size"));
646 res = alpm_list_add(res, str);
648 return(res);
651 /* returns package info as list of strings */
652 static alpm_list_t *create_verbose_row(pmpkg_t *pkg, int install)
654 char *str;
655 double size;
656 const char *label;
657 alpm_list_t *ret = NULL;
658 pmdb_t *ldb = alpm_option_get_localdb(config->handle);
660 /* a row consists of the package name, */
661 pm_asprintf(&str, "%s", alpm_pkg_get_name(pkg));
662 ret = alpm_list_add(ret, str);
664 /* old and new versions */
665 if(install) {
666 pmpkg_t *oldpkg = alpm_db_get_pkg(ldb, alpm_pkg_get_name(pkg));
667 pm_asprintf(&str, "%s",
668 oldpkg != NULL ? alpm_pkg_get_version(oldpkg) : "");
669 ret = alpm_list_add(ret, str);
672 pm_asprintf(&str, "%s", alpm_pkg_get_version(pkg));
673 ret = alpm_list_add(ret, str);
675 /* and size */
676 size = humanize_size(alpm_pkg_get_size(pkg), 'M', 1, &label);
677 pm_asprintf(&str, "%.2f %s", size, label);
678 ret = alpm_list_add(ret, str);
680 return(ret);
683 /* prepare a list of pkgs to display */
684 void display_targets(const alpm_list_t *pkgs, int install)
686 char *str;
687 const char *title, *label;
688 double size;
689 const alpm_list_t *i;
690 off_t isize = 0, rsize = 0, dlsize = 0;
691 alpm_list_t *j, *lp, *header = NULL, *targets = NULL;
692 pmdb_t *db_local = alpm_option_get_localdb(config->handle);
694 if(!pkgs) {
695 return;
698 /* gather pkg infos */
699 for(i = pkgs; i; i = alpm_list_next(i)) {
700 pmpkg_t *pkg = alpm_list_getdata(i);
702 if(install) {
703 pmpkg_t *lpkg = alpm_db_get_pkg(db_local, alpm_pkg_get_name(pkg));
704 dlsize += alpm_pkg_download_size(pkg);
705 if(lpkg) {
706 /* add up size of all removed packages */
707 rsize += alpm_pkg_get_isize(lpkg);
710 isize += alpm_pkg_get_isize(pkg);
712 if(config->verbosepkglists) {
713 targets = alpm_list_add(targets, create_verbose_row(pkg, install));
714 } else {
715 pm_asprintf(&str, "%s-%s", alpm_pkg_get_name(pkg),
716 alpm_pkg_get_version(pkg));
717 targets = alpm_list_add(targets, str);
721 /* print to screen */
722 title = install ? _("Targets (%d):") : _("Remove (%d):");
723 pm_asprintf(&str, title, alpm_list_count(pkgs));
725 printf("\n");
726 if(config->verbosepkglists) {
727 header = create_verbose_header(install);
728 if(table_display(str, header, targets) != 0) {
729 config->verbosepkglists = 0;
730 display_targets(pkgs, install);
731 goto out;
733 } else {
734 list_display(str, targets);
736 printf("\n");
738 if(install) {
739 size = humanize_size(dlsize, 'M', 1, &label);
740 printf(_("Total Download Size: %.2f %s\n"), size, label);
741 if(!(config->flags & PM_TRANS_FLAG_DOWNLOADONLY)) {
742 size = humanize_size(isize, 'M', 1, &label);
743 printf(_("Total Installed Size: %.2f %s\n"), size, label);
744 /* only show this net value if different from raw installed size */
745 if(rsize > 0) {
746 size = humanize_size(isize - rsize, 'M', 1, &label);
747 printf(_("Net Upgrade Size: %.2f %s\n"), size, label);
750 } else {
751 size = humanize_size(isize, 'M', 1, &label);
752 printf(_("Total Removed Size: %.2f %s\n"), size, label);
755 out:
756 /* cleanup */
757 if(config->verbosepkglists) {
758 /* targets is a list of lists of strings, free inner lists here */
759 for(j = alpm_list_first(targets); j; j = alpm_list_next(j)) {
760 lp = alpm_list_getdata(j);
761 FREELIST(lp);
763 alpm_list_free(targets);
764 FREELIST(header);
765 } else {
766 FREELIST(targets);
768 free(str);
771 static off_t pkg_get_size(pmpkg_t *pkg)
773 switch(config->op) {
774 case PM_OP_SYNC:
775 return alpm_pkg_download_size(pkg);
776 case PM_OP_UPGRADE:
777 return alpm_pkg_get_size(pkg);
778 default:
779 return alpm_pkg_get_isize(pkg);
783 static char *pkg_get_location(pmpkg_t *pkg)
785 pmdb_t *db;
786 const char *dburl;
787 char *string;
788 switch(config->op) {
789 case PM_OP_SYNC:
790 db = alpm_pkg_get_db(pkg);
791 dburl = alpm_db_get_url(db);
792 if(dburl) {
793 char *pkgurl = NULL;
794 pm_asprintf(&pkgurl, "%s/%s", dburl, alpm_pkg_get_filename(pkg));
795 return pkgurl;
797 case PM_OP_UPGRADE:
798 return strdup(alpm_pkg_get_filename(pkg));
799 default:
800 string = NULL;
801 pm_asprintf(&string, "%s-%s", alpm_pkg_get_name(pkg), alpm_pkg_get_version(pkg));
802 return string;
806 /** Converts sizes in bytes into human readable units.
808 * @param bytes the size in bytes
809 * @param target_unit '\0' or a short label. If equal to one of the short unit
810 * labels ('B', 'K', ...) bytes is converted to target_unit; if '\0', the first
811 * unit which will bring the value to below a threshold of 2048 will be chosen.
812 * @param long_labels whether to use short ("K") or long ("KiB") unit labels
813 * @param label will be set to the appropriate unit label
815 * @return the size in the appropriate unit
817 double humanize_size(off_t bytes, const char target_unit, int long_labels,
818 const char **label)
820 static const char *shortlabels[] = {"B", "K", "M", "G", "T", "P"};
821 static const char *longlabels[] = {"B", "KiB", "MiB", "GiB", "TiB", "PiB"};
822 static const int unitcount = sizeof(shortlabels) / sizeof(shortlabels[0]);
824 const char **labels = long_labels ? longlabels : shortlabels;
825 double val = (double)bytes;
826 int index;
828 for(index = 0; index < unitcount - 1; index++) {
829 if(target_unit != '\0' && shortlabels[index][0] == target_unit) {
830 break;
831 } else if(target_unit == '\0' && val <= 2048.0) {
832 break;
834 val /= 1024.0;
837 if(label) {
838 *label = labels[index];
841 return(val);
844 void print_packages(const alpm_list_t *packages)
846 const alpm_list_t *i;
847 if(!config->print_format) {
848 config->print_format = strdup("%l");
850 for(i = packages; i; i = alpm_list_next(i)) {
851 pmpkg_t *pkg = alpm_list_getdata(i);
852 char *string = strdup(config->print_format);
853 char *temp = string;
854 /* %n : pkgname */
855 if(strstr(temp,"%n")) {
856 string = strreplace(temp, "%n", alpm_pkg_get_name(pkg));
857 free(temp);
858 temp = string;
860 /* %v : pkgver */
861 if(strstr(temp,"%v")) {
862 string = strreplace(temp, "%v", alpm_pkg_get_version(pkg));
863 free(temp);
864 temp = string;
866 /* %l : location */
867 if(strstr(temp,"%l")) {
868 char *pkgloc = pkg_get_location(pkg);
869 string = strreplace(temp, "%l", pkgloc);
870 free(pkgloc);
871 free(temp);
872 temp = string;
874 /* %r : repo */
875 if(strstr(temp,"%r")) {
876 const char *repo = "local";
877 pmdb_t *db = alpm_pkg_get_db(pkg);
878 if(db) {
879 repo = alpm_db_get_name(db);
881 string = strreplace(temp, "%r", repo);
882 free(temp);
883 temp = string;
885 /* %s : size */
886 if(strstr(temp,"%s")) {
887 char *size;
888 pm_asprintf(&size, "%jd", (intmax_t)pkg_get_size(pkg));
889 string = strreplace(temp, "%s", size);
890 free(size);
891 free(temp);
893 printf("%s\n",string);
894 free(string);
898 /* Helper function for comparing strings using the
899 * alpm "compare func" signature */
900 int str_cmp(const void *s1, const void *s2)
902 return strcmp(s1, s2);
905 void display_new_optdepends(pmpkg_t *oldpkg, pmpkg_t *newpkg)
907 alpm_list_t *old = alpm_pkg_get_optdepends(oldpkg);
908 alpm_list_t *new = alpm_pkg_get_optdepends(newpkg);
909 alpm_list_t *optdeps = alpm_list_diff(new,old,str_cmp);
910 if(optdeps) {
911 printf(_("New optional dependencies for %s\n"), alpm_pkg_get_name(newpkg));
912 list_display_linebreak(" ", optdeps);
914 alpm_list_free(optdeps);
917 void display_optdepends(pmpkg_t *pkg)
919 alpm_list_t *optdeps = alpm_pkg_get_optdepends(pkg);
920 if(optdeps) {
921 printf(_("Optional dependencies for %s\n"), alpm_pkg_get_name(pkg));
922 list_display_linebreak(" ", optdeps);
926 static void display_repo_list(const char *dbname, alpm_list_t *list)
928 const char *prefix= " ";
930 printf(":: ");
931 printf(_("Repository %s\n"), dbname);
932 list_display(prefix, list);
935 void select_display(const alpm_list_t *pkglist)
937 const alpm_list_t *i;
938 int nth = 1;
939 alpm_list_t *list = NULL;
940 char *string = NULL;
941 const char *dbname = NULL;
943 for (i = pkglist; i; i = i->next) {
944 pmpkg_t *pkg = alpm_list_getdata(i);
945 pmdb_t *db = alpm_pkg_get_db(pkg);
947 if(!dbname)
948 dbname = alpm_db_get_name(db);
949 if(strcmp(alpm_db_get_name(db), dbname) != 0) {
950 display_repo_list(dbname, list);
951 FREELIST(list);
952 dbname = alpm_db_get_name(db);
954 string = NULL;
955 pm_asprintf(&string, "%d) %s", nth, alpm_pkg_get_name(pkg));
956 list = alpm_list_add(list, string);
957 nth++;
959 display_repo_list(dbname, list);
960 FREELIST(list);
963 static int parseindex(char *s, int *val, int min, int max)
965 char *endptr = NULL;
966 int n = strtol(s, &endptr, 10);
967 if(*endptr == '\0') {
968 if(n < min || n > max) {
969 fprintf(stderr, _("Invalid value: %d is not between %d and %d\n"),
970 n, min, max);
971 return -1;
973 *val = n;
974 return 0;
975 } else {
976 fprintf(stderr, _("Invalid number: %s\n"), s);
977 return -1;
981 static int multiselect_parse(char *array, int count, char *response)
983 char *str, *saveptr;
985 for (str = response; ; str = NULL) {
986 int include = 1;
987 int start, end;
988 char *ends = NULL;
989 char *starts = strtok_r(str, " ", &saveptr);
991 if(starts == NULL) {
992 break;
994 strtrim(starts);
995 int len = strlen(starts);
996 if(len == 0)
997 continue;
999 if(*starts == '^') {
1000 starts++;
1001 len--;
1002 include = 0;
1003 } else if(str) {
1004 /* if first token is including, we unselect all targets */
1005 memset(array, 0, count);
1008 if(len > 1) {
1009 /* check for range */
1010 char *p;
1011 if((p = strchr(starts + 1, '-'))) {
1012 *p = 0;
1013 ends = p + 1;
1017 if(parseindex(starts, &start, 1, count) != 0)
1018 return -1;
1020 if(!ends) {
1021 array[start-1] = include;
1022 } else {
1023 int d;
1024 if(parseindex(ends, &end, start, count) != 0) {
1025 return -1;
1027 for(d = start; d <= end; d++) {
1028 array[d-1] = include;
1033 return 0;
1036 int multiselect_question(char *array, int count)
1038 char response[64];
1039 FILE *stream;
1041 if(config->noconfirm) {
1042 stream = stdout;
1043 } else {
1044 /* Use stderr so questions are always displayed when redirecting output */
1045 stream = stderr;
1048 while(1) {
1049 memset(array, 1, count);
1051 fprintf(stream, "\n");
1052 fprintf(stream, _("Enter a selection (default=all)"));
1053 fprintf(stream, ": ");
1055 if(config->noconfirm) {
1056 fprintf(stream, "\n");
1057 break;
1060 flush_term_input();
1062 if(fgets(response, sizeof(response), stdin)) {
1063 strtrim(response);
1064 if(strlen(response) > 0) {
1065 if(multiselect_parse(array, count, response) == -1) {
1066 /* only loop if user gave an invalid answer */
1067 continue;
1071 break;
1073 return 0;
1076 int select_question(int count)
1078 char response[32];
1079 FILE *stream;
1080 int preset = 1;
1082 if(config->noconfirm) {
1083 stream = stdout;
1084 } else {
1085 /* Use stderr so questions are always displayed when redirecting output */
1086 stream = stderr;
1089 while(1) {
1090 fprintf(stream, "\n");
1091 fprintf(stream, _("Enter a number (default=%d)"), preset);
1092 fprintf(stream, ": ");
1094 if(config->noconfirm) {
1095 fprintf(stream, "\n");
1096 break;
1099 flush_term_input();
1101 if(fgets(response, sizeof(response), stdin)) {
1102 strtrim(response);
1103 if(strlen(response) > 0) {
1104 int n;
1105 if(parseindex(response, &n, 1, count) != 0)
1106 continue;
1107 return (n - 1);
1110 break;
1113 return (preset - 1);
1117 /* presents a prompt and gets a Y/N answer */
1118 static int question(short preset, char *fmt, va_list args)
1120 char response[32];
1121 FILE *stream;
1123 if(config->noconfirm) {
1124 stream = stdout;
1125 } else {
1126 /* Use stderr so questions are always displayed when redirecting output */
1127 stream = stderr;
1130 /* ensure all text makes it to the screen before we prompt the user */
1131 fflush(stdout);
1132 fflush(stderr);
1134 vfprintf(stream, fmt, args);
1136 if(preset) {
1137 fprintf(stream, " %s ", _("[Y/n]"));
1138 } else {
1139 fprintf(stream, " %s ", _("[y/N]"));
1142 if(config->noconfirm) {
1143 fprintf(stream, "\n");
1144 return preset;
1147 fflush(stream);
1148 flush_term_input();
1150 if(fgets(response, sizeof(response), stdin)) {
1151 strtrim(response);
1152 if(strlen(response) == 0) {
1153 return preset;
1156 if(strcasecmp(response, _("Y")) == 0 || strcasecmp(response, _("YES")) == 0) {
1157 return 1;
1158 } else if(strcasecmp(response, _("N")) == 0 || strcasecmp(response, _("NO")) == 0) {
1159 return 0;
1162 return 0;
1165 int yesno(char *fmt, ...)
1167 int ret;
1168 va_list args;
1170 va_start(args, fmt);
1171 ret = question(1, fmt, args);
1172 va_end(args);
1174 return ret;
1177 int noyes(char *fmt, ...)
1179 int ret;
1180 va_list args;
1182 va_start(args, fmt);
1183 ret = question(0, fmt, args);
1184 va_end(args);
1186 return ret;
1189 int pm_printf(pmloglevel_t level, const char *format, ...)
1191 int ret;
1192 va_list args;
1194 /* print the message using va_arg list */
1195 va_start(args, format);
1196 ret = pm_vfprintf(stdout, level, format, args);
1197 va_end(args);
1199 return ret;
1202 int pm_fprintf(FILE *stream, pmloglevel_t level, const char *format, ...)
1204 int ret;
1205 va_list args;
1207 /* print the message using va_arg list */
1208 va_start(args, format);
1209 ret = pm_vfprintf(stream, level, format, args);
1210 va_end(args);
1212 return ret;
1215 int pm_asprintf(char **string, const char *format, ...)
1217 int ret = 0;
1218 va_list args;
1220 /* print the message using va_arg list */
1221 va_start(args, format);
1222 if(vasprintf(string, format, args) == -1) {
1223 pm_fprintf(stderr, PM_LOG_ERROR, _("failed to allocate string\n"));
1224 ret = -1;
1226 va_end(args);
1228 return ret;
1231 int pm_vasprintf(char **string, pmloglevel_t level, const char *format, va_list args)
1233 int ret = 0;
1234 char *msg = NULL;
1236 /* if current logmask does not overlap with level, do not print msg */
1237 if(!(config->logmask & level)) {
1238 return ret;
1241 /* print the message using va_arg list */
1242 ret = vasprintf(&msg, format, args);
1244 /* print a prefix to the message */
1245 switch(level) {
1246 case PM_LOG_ERROR:
1247 pm_asprintf(string, _("error: %s"), msg);
1248 break;
1249 case PM_LOG_WARNING:
1250 pm_asprintf(string, _("warning: %s"), msg);
1251 break;
1252 case PM_LOG_DEBUG:
1253 pm_asprintf(string, "debug: %s", msg);
1254 break;
1255 case PM_LOG_FUNCTION:
1256 pm_asprintf(string, "function: %s", msg);
1257 break;
1258 default:
1259 pm_asprintf(string, "%s", msg);
1260 break;
1262 free(msg);
1264 return ret;
1267 int pm_vfprintf(FILE *stream, pmloglevel_t level, const char *format, va_list args)
1269 int ret = 0;
1271 /* if current logmask does not overlap with level, do not print msg */
1272 if(!(config->logmask & level)) {
1273 return ret;
1276 #if defined(PACMAN_DEBUG)
1277 /* If debug is on, we'll timestamp the output */
1278 if(config->logmask & PM_LOG_DEBUG) {
1279 time_t t;
1280 struct tm *tmp;
1281 char timestr[10] = {0};
1283 t = time(NULL);
1284 tmp = localtime(&t);
1285 strftime(timestr, 9, "%H:%M:%S", tmp);
1286 timestr[8] = '\0';
1288 printf("[%s] ", timestr);
1290 #endif
1292 /* print a prefix to the message */
1293 switch(level) {
1294 case PM_LOG_ERROR:
1295 fprintf(stream, _("error: "));
1296 break;
1297 case PM_LOG_WARNING:
1298 fprintf(stream, _("warning: "));
1299 break;
1300 case PM_LOG_DEBUG:
1301 fprintf(stream, "debug: ");
1302 break;
1303 case PM_LOG_FUNCTION:
1304 fprintf(stream, "function: ");
1305 break;
1306 default:
1307 break;
1310 /* print the message using va_arg list */
1311 ret = vfprintf(stream, format, args);
1312 return ret;
1315 #ifndef HAVE_STRNDUP
1316 /* A quick and dirty implementation derived from glibc */
1317 static size_t strnlen(const char *s, size_t max)
1319 register const char *p;
1320 for(p = s; *p && max--; ++p);
1321 return (p - s);
1324 char *strndup(const char *s, size_t n)
1326 size_t len = strnlen(s, n);
1327 char *new = (char *) malloc(len + 1);
1329 if(new == NULL)
1330 return NULL;
1332 new[len] = '\0';
1333 return (char *)memcpy(new, s, len);
1335 #endif
1337 /* vim: set ts=2 sw=2 noet: */