4 * Copyright (c) 2006-2009 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>
40 #include <alpm_list.h>
48 int trans_init(pmtransflag_t flags
)
52 ret
= alpm_trans_init(flags
, NULL
, NULL
, NULL
);
54 ret
= alpm_trans_init(flags
, cb_trans_evt
, cb_trans_conv
,
59 pm_fprintf(stderr
, PM_LOG_ERROR
, _("failed to init transaction (%s)\n"),
61 if(pm_errno
== PM_ERR_HANDLE_LOCK
) {
62 fprintf(stderr
, _(" if you're sure a package manager is not already\n"
63 " running, you can remove %s\n"), alpm_option_get_lockfile());
70 int trans_release(void)
72 if(alpm_trans_release() == -1) {
73 pm_fprintf(stderr
, PM_LOG_ERROR
, _("failed to release transaction (%s)\n"),
85 return(!config
->print
);
87 return(config
->op_s_clean
|| config
->op_s_sync
||
88 (!config
->group
&& !config
->op_s_info
&& !config
->op_q_list
&&
89 !config
->op_s_search
&& !config
->print
));
95 /* gets the current screen column width */
100 if(ioctl(1, TIOCGSIZE
, &win
) == 0) {
103 #elif defined(TIOCGWINSZ)
105 if(ioctl(1, TIOCGWINSZ
, &win
) == 0) {
112 /* does the same thing as 'rm -rf' */
113 int rmrf(const char *path
)
122 if(errno
== ENOENT
) {
124 } else if(errno
== EPERM
) {
126 } else if(errno
== EISDIR
) {
128 } else if(errno
== ENOTDIR
) {
131 /* not a directory */
135 dirp
= opendir(path
);
139 for(dp
= readdir(dirp
); dp
!= NULL
; dp
= readdir(dirp
)) {
142 sprintf(name
, "%s/%s", path
, dp
->d_name
);
143 if(strcmp(dp
->d_name
, "..") && strcmp(dp
->d_name
, ".")) {
144 errflag
+= rmrf(name
);
156 /** Parse the basename of a program from a path.
157 * Grabbed from the uClibc source.
158 * @param path path to parse basename from
160 * @return everything following the final '/'
162 char *mbasename(const char *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
) = 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
, 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
, 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
);
523 dlsize
+= alpm_pkg_download_size(pkg
);
524 isize
+= alpm_pkg_get_isize(pkg
);
526 /* print the package size with the output if ShowSize option set */
527 if(config
->showsize
) {
529 mbsize
= alpm_pkg_get_size(pkg
) / (1024.0 * 1024.0);
531 asprintf(&str
, "%s-%s [%.2f MB]", alpm_pkg_get_name(pkg
),
532 alpm_pkg_get_version(pkg
), mbsize
);
534 asprintf(&str
, "%s-%s", alpm_pkg_get_name(pkg
),
535 alpm_pkg_get_version(pkg
));
537 targets
= alpm_list_add(targets
, str
);
540 /* Convert byte sizes to MB */
541 mbdlsize
= dlsize
/ (1024.0 * 1024.0);
542 mbisize
= isize
/ (1024.0 * 1024.0);
545 asprintf(&str
, _("Targets (%d):"), alpm_list_count(targets
));
546 list_display(str
, targets
);
550 printf(_("Total Download Size: %.2f MB\n"), mbdlsize
);
551 if(!(config
->flags
& PM_TRANS_FLAG_DOWNLOADONLY
)) {
552 printf(_("Total Installed Size: %.2f MB\n"), mbisize
);
555 asprintf(&str
, _("Remove (%d):"), alpm_list_count(targets
));
556 list_display(str
, targets
);
560 printf(_("Total Removed Size: %.2f MB\n"), mbisize
);
566 static off_t
pkg_get_size(pmpkg_t
*pkg
)
570 return(alpm_pkg_download_size(pkg
));
572 return(alpm_pkg_get_size(pkg
));
574 return(alpm_pkg_get_isize(pkg
));
578 static char *pkg_get_location(pmpkg_t
*pkg
)
585 db
= alpm_pkg_get_db(pkg
);
586 dburl
= alpm_db_get_url(db
);
589 asprintf(&pkgurl
, "%s/%s", dburl
, alpm_pkg_get_filename(pkg
));
593 return(strdup(alpm_pkg_get_filename(pkg
)));
596 asprintf(&string
, "%s-%s", alpm_pkg_get_name(pkg
), alpm_pkg_get_version(pkg
));
601 void print_packages(const alpm_list_t
*packages
)
603 const alpm_list_t
*i
;
604 if(!config
->print_format
) {
605 config
->print_format
= strdup("%l");
607 for(i
= packages
; i
; i
= alpm_list_next(i
)) {
608 pmpkg_t
*pkg
= alpm_list_getdata(i
);
609 char *string
= strdup(config
->print_format
);
612 if(strstr(temp
,"%n")) {
613 string
= strreplace(temp
, "%n", alpm_pkg_get_name(pkg
));
618 if(strstr(temp
,"%v")) {
619 string
= strreplace(temp
, "%v", alpm_pkg_get_version(pkg
));
624 if(strstr(temp
,"%l")) {
625 char *pkgloc
= pkg_get_location(pkg
);
626 string
= strreplace(temp
, "%l", pkgloc
);
632 if(strstr(temp
,"%r")) {
633 const char *repo
= "local";
634 pmdb_t
*db
= alpm_pkg_get_db(pkg
);
636 repo
= alpm_db_get_name(db
);
638 string
= strreplace(temp
, "%r", repo
);
643 if(strstr(temp
,"%s")) {
646 mbsize
= pkg_get_size(pkg
) / (1024.0 * 1024.0);
647 asprintf(&size
, "%.2f", mbsize
);
648 string
= strreplace(temp
, "%s", size
);
653 printf("%s\n",string
);
658 /* Helper function for comparing strings using the
659 * alpm "compare func" signature */
660 int str_cmp(const void *s1
, const void *s2
)
662 return(strcmp(s1
, s2
));
665 void display_new_optdepends(pmpkg_t
*oldpkg
, pmpkg_t
*newpkg
)
667 alpm_list_t
*old
= alpm_pkg_get_optdepends(oldpkg
);
668 alpm_list_t
*new = alpm_pkg_get_optdepends(newpkg
);
669 alpm_list_t
*optdeps
= alpm_list_diff(new,old
,str_cmp
);
671 printf(_("New optional dependencies for %s\n"), alpm_pkg_get_name(newpkg
));
672 list_display_linebreak(" ", optdeps
);
674 alpm_list_free(optdeps
);
677 void display_optdepends(pmpkg_t
*pkg
)
679 alpm_list_t
*optdeps
= alpm_pkg_get_optdepends(pkg
);
681 printf(_("Optional dependencies for %s\n"), alpm_pkg_get_name(pkg
));
682 list_display_linebreak(" ", optdeps
);
686 /* presents a prompt and gets a Y/N answer */
687 static int question(short preset
, char *fmt
, va_list args
)
690 int sresponse
= sizeof(response
)-1;
693 if(config
->noconfirm
) {
696 /* Use stderr so questions are always displayed when redirecting output */
700 vfprintf(stream
, fmt
, args
);
703 fprintf(stream
, " %s ", _("[Y/n]"));
705 fprintf(stream
, " %s ", _("[y/N]"));
708 if(config
->noconfirm
) {
709 fprintf(stream
, "\n");
713 if(fgets(response
, sresponse
, stdin
)) {
715 if(strlen(response
) == 0) {
719 if(!strcasecmp(response
, _("Y")) || !strcasecmp(response
, _("YES"))) {
721 } else if (!strcasecmp(response
, _("N")) || !strcasecmp(response
, _("NO"))) {
728 int yesno(char *fmt
, ...)
734 ret
= question(1, fmt
, args
);
740 int noyes(char *fmt
, ...)
746 ret
= question(0, fmt
, args
);
752 int pm_printf(pmloglevel_t level
, const char *format
, ...)
757 /* print the message using va_arg list */
758 va_start(args
, format
);
759 ret
= pm_vfprintf(stdout
, level
, format
, args
);
765 int pm_fprintf(FILE *stream
, pmloglevel_t level
, const char *format
, ...)
770 /* print the message using va_arg list */
771 va_start(args
, format
);
772 ret
= pm_vfprintf(stream
, level
, format
, args
);
778 int pm_vasprintf(char **string
, pmloglevel_t level
, const char *format
, va_list args
)
783 /* if current logmask does not overlap with level, do not print msg */
784 if(!(config
->logmask
& level
)) {
788 /* print the message using va_arg list */
789 ret
= vasprintf(&msg
, format
, args
);
791 /* print a prefix to the message */
794 asprintf(string
, "debug: %s", msg
);
797 asprintf(string
, _("error: %s"), msg
);
800 asprintf(string
, _("warning: %s"), msg
);
802 case PM_LOG_FUNCTION
:
803 asprintf(string
, _("function: %s"), msg
);
813 int pm_vfprintf(FILE *stream
, pmloglevel_t level
, const char *format
, va_list args
)
817 /* if current logmask does not overlap with level, do not print msg */
818 if(!(config
->logmask
& level
)) {
822 #if defined(PACMAN_DEBUG)
823 /* If debug is on, we'll timestamp the output */
824 if(config
->logmask
& PM_LOG_DEBUG
) {
827 char timestr
[10] = {0};
831 strftime(timestr
, 9, "%H:%M:%S", tmp
);
834 printf("[%s] ", timestr
);
838 /* print a prefix to the message */
841 fprintf(stream
, "debug: ");
844 fprintf(stream
, _("error: "));
847 fprintf(stream
, _("warning: "));
849 case PM_LOG_FUNCTION
:
850 /* TODO we should increase the indent level when this occurs so we can see
851 * program flow easier. It'll be fun */
852 fprintf(stream
, _("function: "));
858 /* print the message using va_arg list */
859 ret
= vfprintf(stream
, format
, args
);
864 /* A quick and dirty implementation derived from glibc */
865 static size_t strnlen(const char *s
, size_t max
)
867 register const char *p
;
868 for(p
= s
; *p
&& max
--; ++p
);
872 char *strndup(const char *s
, size_t n
)
874 size_t len
= strnlen(s
, n
);
875 char *new = (char *) malloc(len
+ 1);
881 return (char *) memcpy(new, s
, len
);
885 /* vim: set ts=2 sw=2 noet: */