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/>.
23 #include <sys/types.h>
24 #include <sys/ioctl.h>
31 #include <stdint.h> /* intmax_t */
41 #include <termios.h> /* tcflush */
45 #include <alpm_list.h>
53 int trans_init(pmtransflag_t flags
)
57 ret
= alpm_trans_init(flags
, NULL
, NULL
, NULL
);
59 ret
= alpm_trans_init(flags
, cb_trans_evt
, cb_trans_conv
,
64 pm_fprintf(stderr
, PM_LOG_ERROR
, _("failed to init transaction (%s)\n"),
66 if(pm_errno
== PM_ERR_HANDLE_LOCK
) {
67 fprintf(stderr
, _(" if you're sure a package manager is not already\n"
68 " running, you can remove %s\n"), alpm_option_get_lockfile());
70 else if(pm_errno
== PM_ERR_DB_VERSION
) {
71 fprintf(stderr
, _(" try running pacman-db-upgrade\n"));
79 int trans_release(void)
81 if(alpm_trans_release() == -1) {
82 pm_fprintf(stderr
, PM_LOG_ERROR
, _("failed to release transaction (%s)\n"),
96 return(!config
->print
);
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
));
106 /* discard unhandled input on the terminal's input buffer */
107 static int flush_term_input(void) {
109 if(isatty(fileno(stdin
))) {
110 return(tcflush(fileno(stdin
), TCIFLUSH
));
118 /* gets the current screen column width */
123 if(ioctl(1, TIOCGSIZE
, &win
) == 0) {
126 #elif defined(TIOCGWINSZ)
128 if(ioctl(1, TIOCGWINSZ
, &win
) == 0) {
135 /* does the same thing as 'rm -rf' */
136 int rmrf(const char *path
)
145 if(errno
== ENOENT
) {
147 } else if(errno
== EPERM
) {
149 } else if(errno
== EISDIR
) {
151 } else if(errno
== ENOTDIR
) {
154 /* not a directory */
158 dirp
= opendir(path
);
162 for(dp
= readdir(dirp
); dp
!= NULL
; dp
= readdir(dirp
)) {
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
);
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
, '/');
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
)
203 /* null or empty path */
204 if(path
== NULL
|| path
== '\0') {
209 last
= strrchr(ret
, '/');
212 /* we found a '/', so terminate our string */
221 /* output a string, but wrap words properly with a specified indentation
223 void indentprint(const char *str
, int indent
)
228 const int cols
= getcols(0);
234 /* if we're not a tty, or our tty is not wide enough that wrapping even makes
235 * sense, print without indenting */
236 if(cols
== 0 || indent
> cols
) {
241 len
= strlen(str
) + 1;
242 wcstr
= calloc(len
, sizeof(wchar_t));
243 len
= mbstowcs(wcstr
, str
, len
);
253 const wchar_t *q
, *next
;
255 if(p
== NULL
|| *p
== L
' ') continue;
256 next
= wcschr(p
, L
' ');
258 next
= p
+ wcslen(p
);
260 /* len captures # cols */
264 len
+= wcwidth(*q
++);
266 if(len
> (cols
- cidx
- 1)) {
267 /* wrap to a newline and reindent */
268 printf("\n%-*s", indent
, "");
276 printf("%lc", (wint_t)*p
);
283 /* Convert a string to uppercase
285 char *strtoupper(char *str
)
290 (*ptr
) = (char)toupper((unsigned char)*ptr
);
296 /* Trim whitespace and newlines from a string
298 char *strtrim(char *str
)
302 if(str
== NULL
|| *str
== '\0') {
303 /* string is empty, so we're done. */
307 while(isspace((unsigned char)*pch
)) {
311 memmove(str
, pch
, (strlen(pch
) + 1));
314 /* check if there wasn't anything but whitespace in the string. */
319 pch
= (str
+ (strlen(str
) - 1));
320 while(isspace((unsigned char)*pch
)) {
328 /* Replace all occurances of 'needle' with 'replace' in 'str', returning
329 * a new string (must be free'd) */
330 char *strreplace(const char *str
, const char *needle
, const char *replace
)
332 const char *p
= NULL
, *q
= NULL
;
333 char *newstr
= NULL
, *newp
= NULL
;
334 alpm_list_t
*i
= NULL
, *list
= NULL
;
335 size_t needlesz
= strlen(needle
), replacesz
= strlen(replace
);
343 q
= strstr(p
, needle
);
345 list
= alpm_list_add(list
, (char *)q
);
347 q
= strstr(p
, needle
);
350 /* no occurences of needle found */
354 /* size of new string = size of old string + "number of occurences of needle"
355 * x "size difference between replace and needle" */
356 newsz
= strlen(str
) + 1 +
357 alpm_list_count(list
) * (replacesz
- needlesz
);
358 newstr
= malloc(newsz
);
366 for(i
= list
; i
; i
= alpm_list_next(i
)) {
367 q
= alpm_list_getdata(i
);
369 /* add chars between this occurence and last occurence, if any */
370 strncpy(newp
, p
, (size_t)(q
- p
));
373 strncpy(newp
, replace
, replacesz
);
377 alpm_list_free(list
);
380 /* add the rest of 'p' */
389 /** Splits a string into a list of strings using the chosen character as
392 * @param str the string to split
393 * @param splitchar the character to split at
395 * @return a list containing the duplicated strings
397 alpm_list_t
*strsplit(const char *str
, const char splitchar
)
399 alpm_list_t
*list
= NULL
;
400 const char *prev
= str
;
403 while((str
= strchr(str
, splitchar
))) {
404 dup
= strndup(prev
, (size_t)(str
- prev
));
408 list
= alpm_list_add(list
, dup
);
418 list
= alpm_list_add(list
, dup
);
423 static int string_length(const char *s
)
431 /* len goes from # bytes -> # chars -> # cols */
433 wcstr
= calloc(len
, sizeof(wchar_t));
434 len
= mbstowcs(wcstr
, s
, len
);
435 len
= wcswidth(wcstr
, len
);
441 void string_display(const char *title
, const char *string
)
444 printf("%s ", title
);
446 if(string
== NULL
|| string
[0] == '\0') {
449 /* compute the length of title + a space */
450 int len
= string_length(title
) + 1;
451 indentprint(string
, len
);
456 void list_display(const char *title
, const alpm_list_t
*list
)
458 const alpm_list_t
*i
;
462 len
= string_length(title
) + 1;
463 printf("%s ", title
);
467 printf("%s\n", _("None"));
469 const int maxcols
= getcols(0);
471 const char *str
= alpm_list_getdata(list
);
473 cols
+= string_length(str
);
474 for(i
= alpm_list_next(list
); i
; i
= alpm_list_next(i
)) {
475 const char *str
= alpm_list_getdata(i
);
476 int s
= string_length(str
);
477 /* wrap only if we have enough usable column space */
478 if(maxcols
> len
&& cols
+ s
+ 2 >= maxcols
) {
482 for (j
= 1; j
<= len
; j
++) {
485 } else if (cols
!= len
) {
486 /* 2 spaces are added if this is not the first element on a line. */
497 void list_display_linebreak(const char *title
, const alpm_list_t
*list
)
499 const alpm_list_t
*i
;
503 len
= string_length(title
) + 1;
504 printf("%s ", title
);
508 printf("%s\n", _("None"));
510 /* Print the first element */
511 indentprint((const char *) alpm_list_getdata(list
), len
);
514 for(i
= alpm_list_next(list
); i
; i
= alpm_list_next(i
)) {
516 for(j
= 1; j
<= len
; j
++) {
519 indentprint((const char *) alpm_list_getdata(i
), len
);
524 /* prepare a list of pkgs to display */
525 void display_targets(const alpm_list_t
*pkgs
, int install
)
528 const alpm_list_t
*i
;
529 off_t isize
= 0, dlsize
= 0;
530 double mbisize
= 0.0, mbdlsize
= 0.0;
531 alpm_list_t
*targets
= NULL
;
538 for(i
= pkgs
; i
; i
= alpm_list_next(i
)) {
539 pmpkg_t
*pkg
= alpm_list_getdata(i
);
542 dlsize
+= alpm_pkg_download_size(pkg
);
544 isize
+= alpm_pkg_get_isize(pkg
);
546 /* print the package size with the output if ShowSize option set */
547 if(config
->showsize
) {
548 double mbsize
= (double)alpm_pkg_get_size(pkg
) / (1024.0 * 1024.0);
550 pm_asprintf(&str
, "%s-%s [%.2f MB]", alpm_pkg_get_name(pkg
),
551 alpm_pkg_get_version(pkg
), mbsize
);
553 pm_asprintf(&str
, "%s-%s", alpm_pkg_get_name(pkg
),
554 alpm_pkg_get_version(pkg
));
556 targets
= alpm_list_add(targets
, str
);
559 /* Convert byte sizes to MB */
560 mbdlsize
= (double)dlsize
/ (1024.0 * 1024.0);
561 mbisize
= (double)isize
/ (1024.0 * 1024.0);
564 pm_asprintf(&str
, _("Targets (%d):"), alpm_list_count(targets
));
565 list_display(str
, targets
);
569 printf(_("Total Download Size: %.2f MB\n"), mbdlsize
);
570 if(!(config
->flags
& PM_TRANS_FLAG_DOWNLOADONLY
)) {
571 printf(_("Total Installed Size: %.2f MB\n"), mbisize
);
574 pm_asprintf(&str
, _("Remove (%d):"), alpm_list_count(targets
));
575 list_display(str
, targets
);
579 printf(_("Total Removed Size: %.2f MB\n"), mbisize
);
585 static off_t
pkg_get_size(pmpkg_t
*pkg
)
589 return(alpm_pkg_download_size(pkg
));
591 return(alpm_pkg_get_size(pkg
));
593 return(alpm_pkg_get_isize(pkg
));
597 static char *pkg_get_location(pmpkg_t
*pkg
)
604 db
= alpm_pkg_get_db(pkg
);
605 dburl
= alpm_db_get_url(db
);
608 pm_asprintf(&pkgurl
, "%s/%s", dburl
, alpm_pkg_get_filename(pkg
));
612 return(strdup(alpm_pkg_get_filename(pkg
)));
615 pm_asprintf(&string
, "%s-%s", alpm_pkg_get_name(pkg
), alpm_pkg_get_version(pkg
));
620 void print_packages(const alpm_list_t
*packages
)
622 const alpm_list_t
*i
;
623 if(!config
->print_format
) {
624 config
->print_format
= strdup("%l");
626 for(i
= packages
; i
; i
= alpm_list_next(i
)) {
627 pmpkg_t
*pkg
= alpm_list_getdata(i
);
628 char *string
= strdup(config
->print_format
);
631 if(strstr(temp
,"%n")) {
632 string
= strreplace(temp
, "%n", alpm_pkg_get_name(pkg
));
637 if(strstr(temp
,"%v")) {
638 string
= strreplace(temp
, "%v", alpm_pkg_get_version(pkg
));
643 if(strstr(temp
,"%l")) {
644 char *pkgloc
= pkg_get_location(pkg
);
645 string
= strreplace(temp
, "%l", pkgloc
);
651 if(strstr(temp
,"%r")) {
652 const char *repo
= "local";
653 pmdb_t
*db
= alpm_pkg_get_db(pkg
);
655 repo
= alpm_db_get_name(db
);
657 string
= strreplace(temp
, "%r", repo
);
662 if(strstr(temp
,"%s")) {
664 pm_asprintf(&size
, "%jd", (intmax_t)pkg_get_size(pkg
));
665 string
= strreplace(temp
, "%s", size
);
669 printf("%s\n",string
);
674 /* Helper function for comparing strings using the
675 * alpm "compare func" signature */
676 int str_cmp(const void *s1
, const void *s2
)
678 return(strcmp(s1
, s2
));
681 void display_new_optdepends(pmpkg_t
*oldpkg
, pmpkg_t
*newpkg
)
683 alpm_list_t
*old
= alpm_pkg_get_optdepends(oldpkg
);
684 alpm_list_t
*new = alpm_pkg_get_optdepends(newpkg
);
685 alpm_list_t
*optdeps
= alpm_list_diff(new,old
,str_cmp
);
687 printf(_("New optional dependencies for %s\n"), alpm_pkg_get_name(newpkg
));
688 list_display_linebreak(" ", optdeps
);
690 alpm_list_free(optdeps
);
693 void display_optdepends(pmpkg_t
*pkg
)
695 alpm_list_t
*optdeps
= alpm_pkg_get_optdepends(pkg
);
697 printf(_("Optional dependencies for %s\n"), alpm_pkg_get_name(pkg
));
698 list_display_linebreak(" ", optdeps
);
702 static void display_repo_list(const char *dbname
, alpm_list_t
*list
)
704 const char *prefix
= " ";
707 printf(_("Repository %s\n"), dbname
);
708 list_display(prefix
, list
);
711 void select_display(const alpm_list_t
*pkglist
)
713 const alpm_list_t
*i
;
715 alpm_list_t
*list
= NULL
;
717 const char *dbname
= NULL
;
719 for (i
= pkglist
; i
; i
= i
->next
) {
720 pmpkg_t
*pkg
= alpm_list_getdata(i
);
721 pmdb_t
*db
= alpm_pkg_get_db(pkg
);
724 dbname
= alpm_db_get_name(db
);
725 if(strcmp(alpm_db_get_name(db
), dbname
) != 0) {
726 display_repo_list(dbname
, list
);
728 dbname
= alpm_db_get_name(db
);
731 pm_asprintf(&string
, "%d) %s", nth
, alpm_pkg_get_name(pkg
));
732 list
= alpm_list_add(list
, string
);
735 display_repo_list(dbname
, list
);
739 static int parseindex(char *s
, int *val
, int min
, int max
)
742 int n
= strtol(s
, &endptr
, 10);
743 if(*endptr
== '\0') {
744 if(n
< min
|| n
> max
) {
745 fprintf(stderr
, _("Invalid value: %d is not between %d and %d\n"),
752 fprintf(stderr
, _("Invalid number: %s\n"), s
);
757 static int multiselect_parse(char *array
, int count
, char *response
)
761 for (str
= response
; ; str
= NULL
) {
765 char *starts
= strtok_r(str
, " ", &saveptr
);
767 if (starts
== NULL
) {
771 int len
= strlen(starts
);
775 if (*starts
== '^') {
780 /* if first token is including, we unselect all targets */
781 memset(array
, 0, count
);
785 /* check for range */
787 if((p
= strchr(starts
+ 1, '-'))) {
793 if(parseindex(starts
, &start
, 1, count
) != 0)
797 array
[start
-1] = include
;
800 if(parseindex(ends
, &end
, start
, count
) != 0) {
803 for(d
= start
; d
<= end
; d
++) {
804 array
[d
-1] = include
;
812 int multiselect_question(char *array
, int count
)
814 char *response
, *lastchar
;
816 size_t response_len
= 64;
818 if(config
->noconfirm
) {
821 /* Use stderr so questions are always displayed when redirecting output */
825 response
= malloc(response_len
);
829 lastchar
= response
+ response_len
- 1;
830 /* sentinel byte to later see if we filled up the entire string */
834 memset(array
, 1, count
);
836 fprintf(stream
, "\n");
837 fprintf(stream
, _("Enter a selection (default=all)"));
838 fprintf(stream
, ": ");
841 if(config
->noconfirm
) {
842 fprintf(stream
, "\n");
848 if(fgets(response
, response_len
, stdin
)) {
849 const size_t response_incr
= 64;
850 /* handle buffer not being large enough to read full line case */
851 while(*lastchar
== '\0' && lastchar
[-1] != '\n') {
852 response_len
+= response_incr
;
853 response
= realloc(response
, response_len
);
857 lastchar
= response
+ response_len
- 1;
860 if(fgets(response
+ response_len
- response_incr
- 1,
861 response_incr
+ 1, stdin
) == 0) {
867 if(strlen(response
) > 0) {
868 if(multiselect_parse(array
, count
, response
) == -1) {
869 /* only loop if user gave an invalid answer */
884 int select_question(int count
)
890 if(config
->noconfirm
) {
893 /* Use stderr so questions are always displayed when redirecting output */
898 fprintf(stream
, "\n");
899 fprintf(stream
, _("Enter a number (default=%d)"), preset
);
900 fprintf(stream
, ": ");
902 if(config
->noconfirm
) {
903 fprintf(stream
, "\n");
909 if(fgets(response
, sizeof(response
), stdin
)) {
911 if(strlen(response
) > 0) {
913 if(parseindex(response
, &n
, 1, count
) != 0)
925 /* presents a prompt and gets a Y/N answer */
926 static int question(short preset
, char *fmt
, va_list args
)
931 if(config
->noconfirm
) {
934 /* Use stderr so questions are always displayed when redirecting output */
938 /* ensure all text makes it to the screen before we prompt the user */
942 vfprintf(stream
, fmt
, args
);
945 fprintf(stream
, " %s ", _("[Y/n]"));
947 fprintf(stream
, " %s ", _("[y/N]"));
950 if(config
->noconfirm
) {
951 fprintf(stream
, "\n");
958 if(fgets(response
, sizeof(response
), stdin
)) {
960 if(strlen(response
) == 0) {
964 if(strcasecmp(response
, _("Y")) == 0 || strcasecmp(response
, _("YES")) == 0) {
966 } else if (strcasecmp(response
, _("N")) == 0 || strcasecmp(response
, _("NO")) == 0) {
973 int yesno(char *fmt
, ...)
979 ret
= question(1, fmt
, args
);
985 int noyes(char *fmt
, ...)
991 ret
= question(0, fmt
, args
);
997 int pm_printf(pmloglevel_t level
, const char *format
, ...)
1002 /* print the message using va_arg list */
1003 va_start(args
, format
);
1004 ret
= pm_vfprintf(stdout
, level
, format
, args
);
1010 int pm_fprintf(FILE *stream
, pmloglevel_t level
, const char *format
, ...)
1015 /* print the message using va_arg list */
1016 va_start(args
, format
);
1017 ret
= pm_vfprintf(stream
, level
, format
, args
);
1023 int pm_asprintf(char **string
, const char *format
, ...)
1028 /* print the message using va_arg list */
1029 va_start(args
, format
);
1030 if(vasprintf(string
, format
, args
) == -1) {
1031 pm_fprintf(stderr
, PM_LOG_ERROR
, _("failed to allocate string\n"));
1039 int pm_vasprintf(char **string
, pmloglevel_t level
, const char *format
, va_list args
)
1044 /* if current logmask does not overlap with level, do not print msg */
1045 if(!(config
->logmask
& level
)) {
1049 /* print the message using va_arg list */
1050 ret
= vasprintf(&msg
, format
, args
);
1052 /* print a prefix to the message */
1055 pm_asprintf(string
, _("error: %s"), msg
);
1057 case PM_LOG_WARNING
:
1058 pm_asprintf(string
, _("warning: %s"), msg
);
1061 pm_asprintf(string
, "debug: %s", msg
);
1063 case PM_LOG_FUNCTION
:
1064 pm_asprintf(string
, "function: %s", msg
);
1067 pm_asprintf(string
, "%s", msg
);
1075 int pm_vfprintf(FILE *stream
, pmloglevel_t level
, const char *format
, va_list args
)
1079 /* if current logmask does not overlap with level, do not print msg */
1080 if(!(config
->logmask
& level
)) {
1084 #if defined(PACMAN_DEBUG)
1085 /* If debug is on, we'll timestamp the output */
1086 if(config
->logmask
& PM_LOG_DEBUG
) {
1089 char timestr
[10] = {0};
1092 tmp
= localtime(&t
);
1093 strftime(timestr
, 9, "%H:%M:%S", tmp
);
1096 printf("[%s] ", timestr
);
1100 /* print a prefix to the message */
1103 fprintf(stream
, _("error: "));
1105 case PM_LOG_WARNING
:
1106 fprintf(stream
, _("warning: "));
1109 fprintf(stream
, "debug: ");
1111 case PM_LOG_FUNCTION
:
1112 fprintf(stream
, "function: ");
1118 /* print the message using va_arg list */
1119 ret
= vfprintf(stream
, format
, args
);
1123 #ifndef HAVE_STRNDUP
1124 /* A quick and dirty implementation derived from glibc */
1125 static size_t strnlen(const char *s
, size_t max
)
1127 register const char *p
;
1128 for(p
= s
; *p
&& max
--; ++p
);
1132 char *strndup(const char *s
, size_t n
)
1134 size_t len
= strnlen(s
, n
);
1135 char *new = (char *) malloc(len
+ 1);
1141 return (char *) memcpy(new, s
, len
);
1145 /* vim: set ts=2 sw=2 noet: */