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 */
42 #include <alpm_list.h>
50 int trans_init(pmtransflag_t flags
)
54 ret
= alpm_trans_init(flags
, NULL
, NULL
, NULL
);
56 ret
= alpm_trans_init(flags
, cb_trans_evt
, cb_trans_conv
,
61 pm_fprintf(stderr
, PM_LOG_ERROR
, _("failed to init transaction (%s)\n"),
63 if(pm_errno
== PM_ERR_HANDLE_LOCK
) {
64 fprintf(stderr
, _(" if you're sure a package manager is not already\n"
65 " running, you can remove %s\n"), alpm_option_get_lockfile());
67 else if(pm_errno
== PM_ERR_DB_VERSION
) {
68 fprintf(stderr
, _(" try running pacman-db-upgrade\n"));
76 int trans_release(void)
78 if(alpm_trans_release() == -1) {
79 pm_fprintf(stderr
, PM_LOG_ERROR
, _("failed to release transaction (%s)\n"),
93 return(!config
->print
);
95 return(config
->op_s_clean
|| config
->op_s_sync
||
96 (!config
->group
&& !config
->op_s_info
&& !config
->op_q_list
&&
97 !config
->op_s_search
&& !config
->print
));
103 /* gets the current screen column width */
108 if(ioctl(1, TIOCGSIZE
, &win
) == 0) {
111 #elif defined(TIOCGWINSZ)
113 if(ioctl(1, TIOCGWINSZ
, &win
) == 0) {
120 /* does the same thing as 'rm -rf' */
121 int rmrf(const char *path
)
130 if(errno
== ENOENT
) {
132 } else if(errno
== EPERM
) {
134 } else if(errno
== EISDIR
) {
136 } else if(errno
== ENOTDIR
) {
139 /* not a directory */
143 dirp
= opendir(path
);
147 for(dp
= readdir(dirp
); dp
!= NULL
; dp
= readdir(dirp
)) {
150 sprintf(name
, "%s/%s", path
, dp
->d_name
);
151 if(strcmp(dp
->d_name
, "..") != 0 && strcmp(dp
->d_name
, ".") != 0) {
152 errflag
+= rmrf(name
);
164 /** Parse the basename of a program from a path.
165 * @param path path to parse basename from
167 * @return everything following the final '/'
169 const char *mbasename(const char *path
)
171 const char *last
= strrchr(path
, '/');
178 /** Parse the dirname of a program from a path.
179 * The path returned should be freed.
180 * @param path path to parse dirname from
182 * @return everything preceding the final '/'
184 char *mdirname(const char *path
)
188 /* null or empty path */
189 if(path
== NULL
|| path
== '\0') {
194 last
= strrchr(ret
, '/');
197 /* we found a '/', so terminate our string */
206 /* output a string, but wrap words properly with a specified indentation
208 void indentprint(const char *str
, int indent
)
220 /* if we're not a tty, print without indenting */
226 len
= strlen(str
) + 1;
227 wcstr
= calloc(len
, sizeof(wchar_t));
228 len
= mbstowcs(wcstr
, str
, len
);
238 const wchar_t *q
, *next
;
240 if(p
== NULL
|| *p
== L
' ') continue;
241 next
= wcschr(p
, L
' ');
243 next
= p
+ wcslen(p
);
245 /* len captures # cols */
249 len
+= wcwidth(*q
++);
251 if(len
> (cols
- cidx
- 1)) {
252 /* wrap to a newline and reindent */
253 printf("\n%-*s", indent
, "");
261 printf("%lc", (wint_t)*p
);
268 /* Convert a string to uppercase
270 char *strtoupper(char *str
)
275 (*ptr
) = (char)toupper((unsigned char)*ptr
);
281 /* Trim whitespace and newlines from a string
283 char *strtrim(char *str
)
287 if(str
== NULL
|| *str
== '\0') {
288 /* string is empty, so we're done. */
292 while(isspace((unsigned char)*pch
)) {
296 memmove(str
, pch
, (strlen(pch
) + 1));
299 /* check if there wasn't anything but whitespace in the string. */
304 pch
= (str
+ (strlen(str
) - 1));
305 while(isspace((unsigned char)*pch
)) {
313 /* Replace all occurances of 'needle' with 'replace' in 'str', returning
314 * a new string (must be free'd) */
315 char *strreplace(const char *str
, const char *needle
, const char *replace
)
317 const char *p
= NULL
, *q
= NULL
;
318 char *newstr
= NULL
, *newp
= NULL
;
319 alpm_list_t
*i
= NULL
, *list
= NULL
;
320 size_t needlesz
= strlen(needle
), replacesz
= strlen(replace
);
328 q
= strstr(p
, needle
);
330 list
= alpm_list_add(list
, (char *)q
);
332 q
= strstr(p
, needle
);
335 /* no occurences of needle found */
339 /* size of new string = size of old string + "number of occurences of needle"
340 * x "size difference between replace and needle" */
341 newsz
= strlen(str
) + 1 +
342 alpm_list_count(list
) * (replacesz
- needlesz
);
343 newstr
= malloc(newsz
);
351 for(i
= list
; i
; i
= alpm_list_next(i
)) {
352 q
= alpm_list_getdata(i
);
354 /* add chars between this occurence and last occurence, if any */
355 strncpy(newp
, p
, (size_t)(q
- p
));
358 strncpy(newp
, replace
, replacesz
);
362 alpm_list_free(list
);
365 /* add the rest of 'p' */
374 /** Splits a string into a list of strings using the chosen character as
377 * @param str the string to split
378 * @param splitchar the character to split at
380 * @return a list containing the duplicated strings
382 alpm_list_t
*strsplit(const char *str
, const char splitchar
)
384 alpm_list_t
*list
= NULL
;
385 const char *prev
= str
;
388 while((str
= strchr(str
, splitchar
))) {
389 dup
= strndup(prev
, (size_t)(str
- prev
));
393 list
= alpm_list_add(list
, dup
);
403 list
= alpm_list_add(list
, dup
);
408 static int string_length(const char *s
)
416 /* len goes from # bytes -> # chars -> # cols */
418 wcstr
= calloc(len
, sizeof(wchar_t));
419 len
= mbstowcs(wcstr
, s
, len
);
420 len
= wcswidth(wcstr
, len
);
426 void string_display(const char *title
, const char *string
)
431 printf("%s ", title
);
433 if(string
== NULL
|| string
[0] == '\0') {
436 /* compute the length of title + a space */
437 len
= string_length(title
) + 1;
438 indentprint(string
, len
);
443 void list_display(const char *title
, const alpm_list_t
*list
)
445 const alpm_list_t
*i
;
449 len
= string_length(title
) + 1;
450 printf("%s ", title
);
454 printf("%s\n", _("None"));
456 for(i
= list
, cols
= len
; i
; i
= alpm_list_next(i
)) {
457 char *str
= alpm_list_getdata(i
);
458 int s
= string_length(str
);
459 int maxcols
= getcols();
460 if(maxcols
> 0 && (cols
+ s
+ 2) >= maxcols
) {
464 for (j
= 1; j
<= len
; j
++) {
467 } else if (cols
!= len
) {
468 /* 2 spaces are added if this is not the first element on a line. */
479 void list_display_linebreak(const char *title
, const alpm_list_t
*list
)
481 const alpm_list_t
*i
;
485 len
= string_length(title
) + 1;
486 printf("%s ", title
);
490 printf("%s\n", _("None"));
492 /* Print the first element */
493 indentprint((const char *) alpm_list_getdata(list
), len
);
496 for(i
= alpm_list_next(list
); i
; i
= alpm_list_next(i
)) {
498 for(j
= 1; j
<= len
; j
++) {
501 indentprint((const char *) alpm_list_getdata(i
), len
);
506 /* prepare a list of pkgs to display */
507 void display_targets(const alpm_list_t
*pkgs
, int install
)
510 const alpm_list_t
*i
;
511 off_t isize
= 0, dlsize
= 0;
512 double mbisize
= 0.0, mbdlsize
= 0.0;
513 alpm_list_t
*targets
= NULL
;
520 for(i
= pkgs
; i
; i
= alpm_list_next(i
)) {
521 pmpkg_t
*pkg
= alpm_list_getdata(i
);
524 dlsize
+= alpm_pkg_download_size(pkg
);
526 isize
+= alpm_pkg_get_isize(pkg
);
528 /* print the package size with the output if ShowSize option set */
529 if(config
->showsize
) {
530 double mbsize
= (double)alpm_pkg_get_size(pkg
) / (1024.0 * 1024.0);
532 pm_asprintf(&str
, "%s-%s [%.2f MB]", alpm_pkg_get_name(pkg
),
533 alpm_pkg_get_version(pkg
), mbsize
);
535 pm_asprintf(&str
, "%s-%s", alpm_pkg_get_name(pkg
),
536 alpm_pkg_get_version(pkg
));
538 targets
= alpm_list_add(targets
, str
);
541 /* Convert byte sizes to MB */
542 mbdlsize
= (double)dlsize
/ (1024.0 * 1024.0);
543 mbisize
= (double)isize
/ (1024.0 * 1024.0);
546 pm_asprintf(&str
, _("Targets (%d):"), alpm_list_count(targets
));
547 list_display(str
, targets
);
551 printf(_("Total Download Size: %.2f MB\n"), mbdlsize
);
552 if(!(config
->flags
& PM_TRANS_FLAG_DOWNLOADONLY
)) {
553 printf(_("Total Installed Size: %.2f MB\n"), mbisize
);
556 pm_asprintf(&str
, _("Remove (%d):"), alpm_list_count(targets
));
557 list_display(str
, targets
);
561 printf(_("Total Removed Size: %.2f MB\n"), mbisize
);
567 static off_t
pkg_get_size(pmpkg_t
*pkg
)
571 return(alpm_pkg_download_size(pkg
));
573 return(alpm_pkg_get_size(pkg
));
575 return(alpm_pkg_get_isize(pkg
));
579 static char *pkg_get_location(pmpkg_t
*pkg
)
586 db
= alpm_pkg_get_db(pkg
);
587 dburl
= alpm_db_get_url(db
);
590 pm_asprintf(&pkgurl
, "%s/%s", dburl
, alpm_pkg_get_filename(pkg
));
594 return(strdup(alpm_pkg_get_filename(pkg
)));
597 pm_asprintf(&string
, "%s-%s", alpm_pkg_get_name(pkg
), alpm_pkg_get_version(pkg
));
602 void print_packages(const alpm_list_t
*packages
)
604 const alpm_list_t
*i
;
605 if(!config
->print_format
) {
606 config
->print_format
= strdup("%l");
608 for(i
= packages
; i
; i
= alpm_list_next(i
)) {
609 pmpkg_t
*pkg
= alpm_list_getdata(i
);
610 char *string
= strdup(config
->print_format
);
613 if(strstr(temp
,"%n")) {
614 string
= strreplace(temp
, "%n", alpm_pkg_get_name(pkg
));
619 if(strstr(temp
,"%v")) {
620 string
= strreplace(temp
, "%v", alpm_pkg_get_version(pkg
));
625 if(strstr(temp
,"%l")) {
626 char *pkgloc
= pkg_get_location(pkg
);
627 string
= strreplace(temp
, "%l", pkgloc
);
633 if(strstr(temp
,"%r")) {
634 const char *repo
= "local";
635 pmdb_t
*db
= alpm_pkg_get_db(pkg
);
637 repo
= alpm_db_get_name(db
);
639 string
= strreplace(temp
, "%r", repo
);
644 if(strstr(temp
,"%s")) {
646 pm_asprintf(&size
, "%jd", (intmax_t)pkg_get_size(pkg
));
647 string
= strreplace(temp
, "%s", size
);
651 printf("%s\n",string
);
656 /* Helper function for comparing strings using the
657 * alpm "compare func" signature */
658 int str_cmp(const void *s1
, const void *s2
)
660 return(strcmp(s1
, s2
));
663 void display_new_optdepends(pmpkg_t
*oldpkg
, pmpkg_t
*newpkg
)
665 alpm_list_t
*old
= alpm_pkg_get_optdepends(oldpkg
);
666 alpm_list_t
*new = alpm_pkg_get_optdepends(newpkg
);
667 alpm_list_t
*optdeps
= alpm_list_diff(new,old
,str_cmp
);
669 printf(_("New optional dependencies for %s\n"), alpm_pkg_get_name(newpkg
));
670 list_display_linebreak(" ", optdeps
);
672 alpm_list_free(optdeps
);
675 void display_optdepends(pmpkg_t
*pkg
)
677 alpm_list_t
*optdeps
= alpm_pkg_get_optdepends(pkg
);
679 printf(_("Optional dependencies for %s\n"), alpm_pkg_get_name(pkg
));
680 list_display_linebreak(" ", optdeps
);
684 static void display_repo_list(const char *dbname
, alpm_list_t
*list
)
686 const char *prefix
= " ";
689 printf(_("Repository %s\n"), dbname
);
690 list_display(prefix
, list
);
693 void select_display(const alpm_list_t
*pkglist
)
695 const alpm_list_t
*i
;
697 alpm_list_t
*list
= NULL
;
699 const char *dbname
= NULL
;
701 for (i
= pkglist
; i
; i
= i
->next
) {
702 pmpkg_t
*pkg
= alpm_list_getdata(i
);
703 pmdb_t
*db
= alpm_pkg_get_db(pkg
);
706 dbname
= alpm_db_get_name(db
);
707 if(strcmp(alpm_db_get_name(db
), dbname
) != 0) {
708 display_repo_list(dbname
, list
);
710 dbname
= alpm_db_get_name(db
);
713 pm_asprintf(&string
, "%d) %s", nth
, alpm_pkg_get_name(pkg
));
714 list
= alpm_list_add(list
, string
);
717 display_repo_list(dbname
, list
);
721 static int parseindex(char *s
, int *val
, int min
, int max
)
724 int n
= strtol(s
, &endptr
, 10);
725 if(*endptr
== '\0') {
726 if(n
< min
|| n
> max
) {
727 fprintf(stderr
, _("Invalid value: %d is not between %d and %d\n"),
734 fprintf(stderr
, _("Invalid number: %s\n"), s
);
739 static int multiselect_parse(char *array
, int count
, char *response
)
743 for (str
= response
; ; str
= NULL
) {
747 char *starts
= strtok_r(str
, " ", &saveptr
);
752 int len
= strlen(starts
);
756 if (*starts
== '^') {
761 /* if first token is including, we unselect all targets */
762 memset(array
, 0, count
);
766 /* check for range */
768 if((p
= strchr(starts
+1, '-'))) {
774 if(parseindex(starts
, &start
, 1, count
) != 0)
778 array
[start
-1] = include
;
780 if(parseindex(ends
, &end
, start
, count
) != 0)
782 for(int d
= start
; d
<= end
; d
++) {
783 array
[d
-1] = include
;
791 int multiselect_question(char *array
, int count
)
796 if(config
->noconfirm
) {
799 /* Use stderr so questions are always displayed when redirecting output */
804 memset(array
, 1, count
);
806 fprintf(stream
, "\n");
807 fprintf(stream
, _("Enter a selection (default=all)"));
808 fprintf(stream
, ": ");
810 if(config
->noconfirm
) {
811 fprintf(stream
, "\n");
815 if(fgets(response
, sizeof(response
), stdin
)) {
817 if(strlen(response
) > 0) {
818 if(multiselect_parse(array
, count
, response
) == -1) {
819 /* only loop if user gave an invalid answer */
829 int select_question(int count
)
835 if(config
->noconfirm
) {
838 /* Use stderr so questions are always displayed when redirecting output */
843 fprintf(stream
, "\n");
844 fprintf(stream
, _("Enter a number (default=%d)"), preset
);
845 fprintf(stream
, ": ");
847 if(config
->noconfirm
) {
848 fprintf(stream
, "\n");
852 if(fgets(response
, sizeof(response
), stdin
)) {
854 if(strlen(response
) > 0) {
856 if(parseindex(response
, &n
, 1, count
) != 0)
868 /* presents a prompt and gets a Y/N answer */
869 static int question(short preset
, char *fmt
, va_list args
)
874 if(config
->noconfirm
) {
877 /* Use stderr so questions are always displayed when redirecting output */
881 vfprintf(stream
, fmt
, args
);
884 fprintf(stream
, " %s ", _("[Y/n]"));
886 fprintf(stream
, " %s ", _("[y/N]"));
889 if(config
->noconfirm
) {
890 fprintf(stream
, "\n");
894 if(fgets(response
, sizeof(response
), stdin
)) {
896 if(strlen(response
) == 0) {
900 if(strcasecmp(response
, _("Y")) == 0 || strcasecmp(response
, _("YES")) == 0) {
902 } else if (strcasecmp(response
, _("N")) == 0 || strcasecmp(response
, _("NO")) == 0) {
909 int yesno(char *fmt
, ...)
915 ret
= question(1, fmt
, args
);
921 int noyes(char *fmt
, ...)
927 ret
= question(0, fmt
, args
);
933 int pm_printf(pmloglevel_t level
, const char *format
, ...)
938 /* print the message using va_arg list */
939 va_start(args
, format
);
940 ret
= pm_vfprintf(stdout
, level
, format
, args
);
946 int pm_fprintf(FILE *stream
, pmloglevel_t level
, const char *format
, ...)
951 /* print the message using va_arg list */
952 va_start(args
, format
);
953 ret
= pm_vfprintf(stream
, level
, format
, args
);
959 int pm_asprintf(char **string
, const char *format
, ...)
964 /* print the message using va_arg list */
965 va_start(args
, format
);
966 if(vasprintf(string
, format
, args
) == -1) {
967 pm_fprintf(stderr
, PM_LOG_ERROR
, _("failed to allocate string\n"));
975 int pm_vasprintf(char **string
, pmloglevel_t level
, const char *format
, va_list args
)
980 /* if current logmask does not overlap with level, do not print msg */
981 if(!(config
->logmask
& level
)) {
985 /* print the message using va_arg list */
986 ret
= vasprintf(&msg
, format
, args
);
988 /* print a prefix to the message */
991 pm_asprintf(string
, _("error: %s"), msg
);
994 pm_asprintf(string
, _("warning: %s"), msg
);
997 pm_asprintf(string
, "debug: %s", msg
);
999 case PM_LOG_FUNCTION
:
1000 pm_asprintf(string
, "function: %s", msg
);
1003 pm_asprintf(string
, "%s", msg
);
1011 int pm_vfprintf(FILE *stream
, pmloglevel_t level
, const char *format
, va_list args
)
1015 /* if current logmask does not overlap with level, do not print msg */
1016 if(!(config
->logmask
& level
)) {
1020 #if defined(PACMAN_DEBUG)
1021 /* If debug is on, we'll timestamp the output */
1022 if(config
->logmask
& PM_LOG_DEBUG
) {
1025 char timestr
[10] = {0};
1028 tmp
= localtime(&t
);
1029 strftime(timestr
, 9, "%H:%M:%S", tmp
);
1032 printf("[%s] ", timestr
);
1036 /* print a prefix to the message */
1039 fprintf(stream
, _("error: "));
1041 case PM_LOG_WARNING
:
1042 fprintf(stream
, _("warning: "));
1045 fprintf(stream
, "debug: ");
1047 case PM_LOG_FUNCTION
:
1048 fprintf(stream
, "function: ");
1054 /* print the message using va_arg list */
1055 ret
= vfprintf(stream
, format
, args
);
1059 #ifndef HAVE_STRNDUP
1060 /* A quick and dirty implementation derived from glibc */
1061 static size_t strnlen(const char *s
, size_t max
)
1063 register const char *p
;
1064 for(p
= s
; *p
&& max
--; ++p
);
1068 char *strndup(const char *s
, size_t n
)
1070 size_t len
= strnlen(s
, n
);
1071 char *new = (char *) malloc(len
+ 1);
1077 return (char *) memcpy(new, s
, len
);
1081 /* vim: set ts=2 sw=2 noet: */