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 */
40 #include <termios.h> /* tcflush */
44 #include <alpm_list.h>
52 int trans_init(alpm_transflag_t flags
, int check_valid
)
56 check_syncdbs(0, check_valid
);
58 ret
= alpm_trans_init(config
->handle
, flags
);
66 void trans_init_error(void)
68 enum _alpm_errno_t err
= alpm_errno(config
->handle
);
69 pm_fprintf(stderr
, ALPM_LOG_ERROR
, _("failed to init transaction (%s)\n"),
71 if(err
== ALPM_ERR_HANDLE_LOCK
) {
72 fprintf(stderr
, _(" if you're sure a package manager is not already\n"
73 " running, you can remove %s\n"),
74 alpm_option_get_lockfile(config
->handle
));
78 int trans_release(void)
80 if(alpm_trans_release(config
->handle
) == -1) {
81 pm_fprintf(stderr
, ALPM_LOG_ERROR
, _("failed to release transaction (%s)\n"),
82 alpm_strerror(alpm_errno(config
->handle
)));
95 return !config
->print
;
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
));
105 int check_syncdbs(size_t need_repos
, int check_valid
)
109 alpm_list_t
*sync_dbs
= alpm_option_get_syncdbs(config
->handle
);
111 if(need_repos
&& sync_dbs
== NULL
) {
112 pm_printf(ALPM_LOG_ERROR
, _("no usable package repositories configured.\n"));
117 /* ensure all known dbs are valid */
118 for(i
= sync_dbs
; i
; i
= alpm_list_next(i
)) {
119 alpm_db_t
*db
= i
->data
;
120 if(alpm_db_get_valid(db
)) {
121 pm_printf(ALPM_LOG_ERROR
, _("database '%s' is not valid (%s)\n"),
122 alpm_db_get_name(db
), alpm_strerror(alpm_errno(config
->handle
)));
130 /* discard unhandled input on the terminal's input buffer */
131 static int flush_term_input(void) {
133 if(isatty(fileno(stdin
))) {
134 return tcflush(fileno(stdin
), TCIFLUSH
);
142 /* gets the current screen column width */
143 unsigned short getcols(void)
145 const unsigned short default_tty
= 80;
146 const unsigned short default_notty
= 0;
147 unsigned short termwidth
= 0;
149 if(!isatty(fileno(stdout
))) {
150 return default_notty
;
155 if(ioctl(1, TIOCGSIZE
, &win
) == 0) {
156 termwidth
= win
.ts_cols
;
158 #elif defined(TIOCGWINSZ)
160 if(ioctl(1, TIOCGWINSZ
, &win
) == 0) {
161 termwidth
= win
.ws_col
;
164 return termwidth
== 0 ? default_tty
: termwidth
;
167 /* does the same thing as 'rm -rf' */
168 int rmrf(const char *path
)
177 if(errno
== ENOENT
) {
179 } else if(errno
== EPERM
) {
181 } else if(errno
== EISDIR
) {
183 } else if(errno
== ENOTDIR
) {
186 /* not a directory */
190 dirp
= opendir(path
);
194 for(dp
= readdir(dirp
); dp
!= NULL
; dp
= readdir(dirp
)) {
197 sprintf(name
, "%s/%s", path
, dp
->d_name
);
198 if(strcmp(dp
->d_name
, "..") != 0 && strcmp(dp
->d_name
, ".") != 0) {
199 errflag
+= rmrf(name
);
211 /** Parse the basename of a program from a path.
212 * @param path path to parse basename from
214 * @return everything following the final '/'
216 const char *mbasename(const char *path
)
218 const char *last
= strrchr(path
, '/');
225 /** Parse the dirname of a program from a path.
226 * The path returned should be freed.
227 * @param path path to parse dirname from
229 * @return everything preceding the final '/'
231 char *mdirname(const char *path
)
235 /* null or empty path */
236 if(path
== NULL
|| path
== '\0') {
241 last
= strrchr(ret
, '/');
244 /* we found a '/', so terminate our string */
253 /* output a string, but wrap words properly with a specified indentation
255 void indentprint(const char *str
, size_t indent
)
260 const unsigned short cols
= getcols();
266 /* if we're not a tty, or our tty is not wide enough that wrapping even makes
267 * sense, print without indenting */
268 if(cols
== 0 || indent
> cols
) {
273 len
= strlen(str
) + 1;
274 wcstr
= calloc(len
, sizeof(wchar_t));
275 len
= mbstowcs(wcstr
, str
, len
);
285 const wchar_t *q
, *next
;
287 if(p
== NULL
|| *p
== L
' ') continue;
288 next
= wcschr(p
, L
' ');
290 next
= p
+ wcslen(p
);
292 /* len captures # cols */
296 len
+= wcwidth(*q
++);
298 if(len
> (cols
- cidx
- 1)) {
299 /* wrap to a newline and reindent */
300 printf("\n%-*s", (int)indent
, "");
308 printf("%lc", (wint_t)*p
);
315 /* Convert a string to uppercase
317 char *strtoupper(char *str
)
322 (*ptr
) = (char)toupper((unsigned char)*ptr
);
328 /* Trim whitespace and newlines from a string
330 char *strtrim(char *str
)
334 if(str
== NULL
|| *str
== '\0') {
335 /* string is empty, so we're done. */
339 while(isspace((unsigned char)*pch
)) {
343 size_t len
= strlen(pch
);
345 memmove(str
, pch
, len
+ 1);
351 /* check if there wasn't anything but whitespace in the string. */
356 pch
= (str
+ (strlen(str
) - 1));
357 while(isspace((unsigned char)*pch
)) {
365 /* Replace all occurances of 'needle' with 'replace' in 'str', returning
366 * a new string (must be free'd) */
367 char *strreplace(const char *str
, const char *needle
, const char *replace
)
369 const char *p
= NULL
, *q
= NULL
;
370 char *newstr
= NULL
, *newp
= NULL
;
371 alpm_list_t
*i
= NULL
, *list
= NULL
;
372 size_t needlesz
= strlen(needle
), replacesz
= strlen(replace
);
380 q
= strstr(p
, needle
);
382 list
= alpm_list_add(list
, (char *)q
);
384 q
= strstr(p
, needle
);
387 /* no occurences of needle found */
391 /* size of new string = size of old string + "number of occurences of needle"
392 * x "size difference between replace and needle" */
393 newsz
= strlen(str
) + 1 +
394 alpm_list_count(list
) * (replacesz
- needlesz
);
395 newstr
= calloc(newsz
, sizeof(char));
402 for(i
= list
; i
; i
= alpm_list_next(i
)) {
403 q
= alpm_list_getdata(i
);
405 /* add chars between this occurence and last occurence, if any */
406 memcpy(newp
, p
, (size_t)(q
- p
));
409 memcpy(newp
, replace
, replacesz
);
413 alpm_list_free(list
);
416 /* add the rest of 'p' */
423 /** Splits a string into a list of strings using the chosen character as
426 * @param str the string to split
427 * @param splitchar the character to split at
429 * @return a list containing the duplicated strings
431 alpm_list_t
*strsplit(const char *str
, const char splitchar
)
433 alpm_list_t
*list
= NULL
;
434 const char *prev
= str
;
437 while((str
= strchr(str
, splitchar
))) {
438 dup
= strndup(prev
, (size_t)(str
- prev
));
442 list
= alpm_list_add(list
, dup
);
452 list
= alpm_list_add(list
, dup
);
457 static size_t string_length(const char *s
)
462 if(!s
|| s
[0] == '\0') {
465 /* len goes from # bytes -> # chars -> # cols */
467 wcstr
= calloc(len
, sizeof(wchar_t));
468 len
= mbstowcs(wcstr
, s
, len
);
469 len
= wcswidth(wcstr
, len
);
475 void string_display(const char *title
, const char *string
)
478 printf("%s ", title
);
480 if(string
== NULL
|| string
[0] == '\0') {
483 /* compute the length of title + a space */
484 size_t len
= string_length(title
) + 1;
485 indentprint(string
, len
);
490 static void table_print_line(const alpm_list_t
*line
,
491 const alpm_list_t
*formats
)
493 const alpm_list_t
*curformat
= formats
;
494 const alpm_list_t
*curcell
= line
;
496 while(curcell
&& curformat
) {
497 printf(alpm_list_getdata(curformat
), alpm_list_getdata(curcell
));
498 curcell
= alpm_list_next(curcell
);
499 curformat
= alpm_list_next(curformat
);
505 /* creates format strings by checking max cell lengths in cols */
506 static alpm_list_t
*table_create_format(const alpm_list_t
*header
,
507 const alpm_list_t
*rows
)
509 alpm_list_t
*formats
= NULL
;
510 const alpm_list_t
*i
;
511 const unsigned short padding
= 2;
512 size_t curcol
, totalcols
, totalwidth
= 0;
515 totalcols
= alpm_list_count(header
);
516 colwidths
= malloc(totalcols
* sizeof(size_t));
520 /* header determines column count and initial values of longest_strs */
521 for(i
= header
, curcol
= 0; i
; i
= alpm_list_next(i
), curcol
++) {
522 colwidths
[curcol
] = string_length(alpm_list_getdata(i
));
525 /* now find the longest string in each column */
526 for(i
= rows
; i
; i
= alpm_list_next(i
)) {
527 /* grab first column of each row and iterate through columns */
528 const alpm_list_t
*j
= alpm_list_getdata(i
);
529 for(curcol
= 0; j
; j
= alpm_list_next(j
), curcol
++) {
530 char *str
= alpm_list_getdata(j
);
531 size_t str_len
= string_length(str
);
533 if(str_len
> colwidths
[curcol
]) {
534 colwidths
[curcol
] = str_len
;
539 /* now use the column width info to generate format strings */
540 for(curcol
= 0; curcol
< totalcols
; curcol
++) {
543 size_t colwidth
= colwidths
[curcol
] + padding
;
544 totalwidth
+= colwidth
;
546 /* right align the last column for a cleaner table display */
547 display
= (curcol
+ 1 < totalcols
) ? "%%-%ds" : "%%%ds";
548 pm_asprintf(&formatstr
, display
, colwidth
);
550 formats
= alpm_list_add(formats
, formatstr
);
555 /* return NULL if terminal is not wide enough */
556 if(totalwidth
> getcols()) {
557 fprintf(stderr
, _("insufficient columns available for table display\n"));
565 /** Displays the list in table format
567 * @param title the tables title
568 * @param header the column headers. column count is determined by the nr
570 * @param rows the rows to display as a list of lists of strings. the outer
571 * list represents the rows, the inner list the cells (= columns)
573 * @return -1 if not enough terminal cols available, else 0
575 int table_display(const char *title
, const alpm_list_t
*header
,
576 const alpm_list_t
*rows
)
578 const alpm_list_t
*i
;
579 alpm_list_t
*formats
;
581 if(rows
== NULL
|| header
== NULL
) {
585 formats
= table_create_format(header
, rows
);
586 if(formats
== NULL
) {
591 printf("%s\n\n", title
);
594 table_print_line(header
, formats
);
597 for(i
= rows
; i
; i
= alpm_list_next(i
)) {
598 table_print_line(alpm_list_getdata(i
), formats
);
605 void list_display(const char *title
, const alpm_list_t
*list
)
607 const alpm_list_t
*i
;
611 len
= string_length(title
) + 1;
612 printf("%s ", title
);
616 printf("%s\n", _("None"));
618 const unsigned short maxcols
= getcols();
620 const char *str
= alpm_list_getdata(list
);
622 cols
+= string_length(str
);
623 for(i
= alpm_list_next(list
); i
; i
= alpm_list_next(i
)) {
624 str
= alpm_list_getdata(i
);
625 size_t s
= string_length(str
);
626 /* wrap only if we have enough usable column space */
627 if(maxcols
> len
&& cols
+ s
+ 2 >= maxcols
) {
631 for (j
= 1; j
<= len
; j
++) {
634 } else if(cols
!= len
) {
635 /* 2 spaces are added if this is not the first element on a line. */
646 void list_display_linebreak(const char *title
, const alpm_list_t
*list
)
651 len
= string_length(title
) + 1;
652 printf("%s ", title
);
656 printf("%s\n", _("None"));
658 const alpm_list_t
*i
;
659 /* Print the first element */
660 indentprint((const char *) alpm_list_getdata(list
), len
);
663 for(i
= alpm_list_next(list
); i
; i
= alpm_list_next(i
)) {
665 for(j
= 1; j
<= len
; j
++) {
668 indentprint((const char *) alpm_list_getdata(i
), len
);
674 void signature_display(const char *title
, alpm_siglist_t
*siglist
)
679 len
= string_length(title
) + 1;
680 printf("%s ", title
);
682 if(siglist
->count
== 0) {
686 for(i
= 0; i
< siglist
->count
; i
++) {
688 const char *status
, *validity
, *name
;
690 alpm_sigresult_t
*result
= siglist
->results
+ i
;
691 /* Don't re-indent the first result */
694 for(j
= 1; j
<= len
; j
++) {
698 switch(result
->status
) {
699 case ALPM_SIGSTATUS_VALID
:
702 case ALPM_SIGSTATUS_KEY_EXPIRED
:
703 status
= _("Key expired");
705 case ALPM_SIGSTATUS_SIG_EXPIRED
:
706 status
= _("Expired");
708 case ALPM_SIGSTATUS_INVALID
:
709 status
= _("Invalid");
711 case ALPM_SIGSTATUS_KEY_UNKNOWN
:
712 status
= _("Key unknown");
714 case ALPM_SIGSTATUS_KEY_DISABLED
:
715 status
= _("Key disabled");
718 status
= _("Signature error");
721 switch(result
->validity
) {
722 case ALPM_SIGVALIDITY_FULL
:
723 validity
= _("full trust");
725 case ALPM_SIGVALIDITY_MARGINAL
:
726 validity
= _("marginal trust");
728 case ALPM_SIGVALIDITY_NEVER
:
729 validity
= _("never trust");
731 case ALPM_SIGVALIDITY_UNKNOWN
:
733 validity
= _("unknown trust");
736 name
= result
->key
.uid
? result
->key
.uid
: result
->key
.fingerprint
;
737 ret
= pm_asprintf(&sigline
, _("%s, %s from \"%s\""),
738 status
, validity
, name
);
740 pm_fprintf(stderr
, ALPM_LOG_ERROR
, _("failed to allocate string\n"));
743 indentprint(sigline
, len
);
750 /* creates a header row for use with table_display */
751 static alpm_list_t
*create_verbose_header(int dl_size
)
753 alpm_list_t
*res
= NULL
;
757 res
= alpm_list_add(res
, str
);
758 str
= _("Old Version");
759 res
= alpm_list_add(res
, str
);
760 str
= _("New Version");
761 res
= alpm_list_add(res
, str
);
762 str
= _("Net Change");
763 res
= alpm_list_add(res
, str
);
765 str
= _("Download Size");
766 res
= alpm_list_add(res
, str
);
772 /* returns package info as list of strings */
773 static alpm_list_t
*create_verbose_row(pm_target_t
*target
, int dl_size
)
779 alpm_list_t
*ret
= NULL
;
781 /* a row consists of the package name, */
782 if(target
->install
) {
783 pm_asprintf(&str
, "%s", alpm_pkg_get_name(target
->install
));
785 pm_asprintf(&str
, "%s", alpm_pkg_get_name(target
->remove
));
787 ret
= alpm_list_add(ret
, str
);
789 /* old and new versions */
790 pm_asprintf(&str
, "%s",
791 target
->remove
!= NULL
? alpm_pkg_get_version(target
->remove
) : "");
792 ret
= alpm_list_add(ret
, str
);
794 pm_asprintf(&str
, "%s",
795 target
->install
!= NULL
? alpm_pkg_get_version(target
->install
) : "");
796 ret
= alpm_list_add(ret
, str
);
799 size
-= target
->remove
? alpm_pkg_get_isize(target
->remove
) : 0;
800 size
+= target
->install
? alpm_pkg_get_isize(target
->install
) : 0;
801 human_size
= humanize_size(size
, 'M', &label
);
802 pm_asprintf(&str
, "%.2f %s", human_size
, label
);
803 ret
= alpm_list_add(ret
, str
);
806 size
= target
->install
? alpm_pkg_download_size(target
->install
) : 0;
807 human_size
= humanize_size(size
, 'M', &label
);
809 pm_asprintf(&str
, "%.2f %s", human_size
, label
);
813 ret
= alpm_list_add(ret
, str
);
819 /* prepare a list of pkgs to display */
820 static void _display_targets(alpm_list_t
*targets
, int verbose
)
825 off_t isize
= 0, rsize
= 0, dlsize
= 0;
826 alpm_list_t
*i
, *rows
= NULL
, *names
= NULL
;
827 int show_dl_size
= config
->op
== PM_OP_SYNC
;
833 /* gather package info */
834 for(i
= targets
; i
; i
= alpm_list_next(i
)) {
835 pm_target_t
*target
= alpm_list_getdata(i
);
837 if(target
->install
) {
838 dlsize
+= alpm_pkg_download_size(target
->install
);
839 isize
+= alpm_pkg_get_isize(target
->install
);
842 /* add up size of all removed packages */
843 rsize
+= alpm_pkg_get_isize(target
->remove
);
846 /* form data for both verbose and non-verbose display */
847 rows
= alpm_list_add(rows
, create_verbose_row(target
, show_dl_size
));
848 if(target
->install
) {
849 pm_asprintf(&str
, "%s-%s", alpm_pkg_get_name(target
->install
),
850 alpm_pkg_get_version(target
->install
));
851 } else if(isize
== 0) {
852 pm_asprintf(&str
, "%s-%s", alpm_pkg_get_name(target
->remove
),
853 alpm_pkg_get_version(target
->remove
));
855 pm_asprintf(&str
, "%s-%s [removal]", alpm_pkg_get_name(target
->remove
),
856 alpm_pkg_get_version(target
->remove
));
858 names
= alpm_list_add(names
, str
);
861 /* print to screen */
862 pm_asprintf(&str
, _("Targets (%d):"), alpm_list_count(targets
));
866 alpm_list_t
*header
= create_verbose_header(show_dl_size
);
867 if(table_display(str
, header
, rows
) != 0) {
868 /* fallback to list display if table wouldn't fit */
869 list_display(str
, names
);
871 alpm_list_free(header
);
873 list_display(str
, names
);
877 /* rows is a list of lists of strings, free inner lists here */
878 for(i
= rows
; i
; i
= alpm_list_next(i
)) {
879 alpm_list_t
*lp
= alpm_list_getdata(i
);
882 alpm_list_free(rows
);
886 if(dlsize
> 0 || config
->op_s_downloadonly
) {
887 size
= humanize_size(dlsize
, 'M', &label
);
888 printf(_("Total Download Size: %.2f %s\n"), size
, label
);
890 if(!config
->op_s_downloadonly
) {
892 size
= humanize_size(isize
, 'M', &label
);
893 printf(_("Total Installed Size: %.2f %s\n"), size
, label
);
895 if(rsize
> 0 && isize
== 0) {
896 size
= humanize_size(rsize
, 'M', &label
);
897 printf(_("Total Removed Size: %.2f %s\n"), size
, label
);
899 /* only show this net value if different from raw installed size */
900 if(isize
> 0 && rsize
> 0) {
901 size
= humanize_size(isize
- rsize
, 'M', &label
);
902 printf(_("Net Upgrade Size: %.2f %s\n"), size
, label
);
907 static int target_cmp(const void *p1
, const void *p2
)
909 const pm_target_t
*targ1
= p1
;
910 const pm_target_t
*targ2
= p2
;
911 /* explicit are always sorted after implicit (e.g. deps, pulled targets) */
912 if(targ1
->is_explicit
!= targ2
->is_explicit
) {
913 return targ1
->is_explicit
> targ2
->is_explicit
;
915 const char *name1
= targ1
->install
?
916 alpm_pkg_get_name(targ1
->install
) : alpm_pkg_get_name(targ1
->remove
);
917 const char *name2
= targ2
->install
?
918 alpm_pkg_get_name(targ2
->install
) : alpm_pkg_get_name(targ2
->remove
);
919 return strcmp(name1
, name2
);
922 static int pkg_cmp(const void *p1
, const void *p2
)
924 /* explicit cast due to (un)necessary removal of const */
925 alpm_pkg_t
*pkg1
= (alpm_pkg_t
*)p1
;
926 alpm_pkg_t
*pkg2
= (alpm_pkg_t
*)p2
;
927 return strcmp(alpm_pkg_get_name(pkg1
), alpm_pkg_get_name(pkg2
));
930 void display_targets(void)
932 alpm_list_t
*i
, *targets
= NULL
;
933 alpm_db_t
*db_local
= alpm_option_get_localdb(config
->handle
);
935 for(i
= alpm_trans_get_add(config
->handle
); i
; i
= alpm_list_next(i
)) {
936 alpm_pkg_t
*pkg
= alpm_list_getdata(i
);
937 pm_target_t
*targ
= calloc(1, sizeof(pm_target_t
));
940 targ
->remove
= alpm_db_get_pkg(db_local
, alpm_pkg_get_name(pkg
));
941 if(alpm_list_find(config
->explicit_adds
, pkg
, pkg_cmp
)) {
942 targ
->is_explicit
= 1;
944 targets
= alpm_list_add(targets
, targ
);
946 for(i
= alpm_trans_get_remove(config
->handle
); i
; i
= alpm_list_next(i
)) {
947 alpm_pkg_t
*pkg
= alpm_list_getdata(i
);
948 pm_target_t
*targ
= calloc(1, sizeof(pm_target_t
));
951 if(alpm_list_find(config
->explicit_removes
, pkg
, pkg_cmp
)) {
952 targ
->is_explicit
= 1;
954 targets
= alpm_list_add(targets
, targ
);
957 targets
= alpm_list_msort(targets
, alpm_list_count(targets
), target_cmp
);
958 _display_targets(targets
, config
->verbosepkglists
);
962 static off_t
pkg_get_size(alpm_pkg_t
*pkg
)
966 return alpm_pkg_download_size(pkg
);
968 return alpm_pkg_get_size(pkg
);
970 return alpm_pkg_get_isize(pkg
);
974 static char *pkg_get_location(alpm_pkg_t
*pkg
)
976 alpm_list_t
*servers
;
980 servers
= alpm_db_get_servers(alpm_pkg_get_db(pkg
));
982 pm_asprintf(&string
, "%s/%s", alpm_list_getdata(servers
),
983 alpm_pkg_get_filename(pkg
));
987 return strdup(alpm_pkg_get_filename(pkg
));
989 pm_asprintf(&string
, "%s-%s", alpm_pkg_get_name(pkg
), alpm_pkg_get_version(pkg
));
994 /** Converts sizes in bytes into human readable units.
996 * @param bytes the size in bytes
997 * @param target_unit '\0' or a short label. If equal to one of the short unit
998 * labels ('B', 'K', ...) bytes is converted to target_unit; if '\0', the first
999 * unit which will bring the value to below a threshold of 2048 will be chosen.
1000 * @param long_labels whether to use short ("K") or long ("KiB") unit labels
1001 * @param label will be set to the appropriate unit label
1003 * @return the size in the appropriate unit
1005 double humanize_size(off_t bytes
, const char target_unit
, const char **label
)
1007 static const char *labels
[] = {"B", "KiB", "MiB", "GiB",
1008 "TiB", "PiB", "EiB", "ZiB", "YiB"};
1009 static const int unitcount
= sizeof(labels
) / sizeof(labels
[0]);
1011 double val
= (double)bytes
;
1014 for(index
= 0; index
< unitcount
- 1; index
++) {
1015 if(target_unit
!= '\0' && labels
[index
][0] == target_unit
) {
1017 } else if(target_unit
== '\0' && val
<= 2048.0 && val
>= -2048.0) {
1024 *label
= labels
[index
];
1030 void print_packages(const alpm_list_t
*packages
)
1032 const alpm_list_t
*i
;
1033 if(!config
->print_format
) {
1034 config
->print_format
= strdup("%l");
1036 for(i
= packages
; i
; i
= alpm_list_next(i
)) {
1037 alpm_pkg_t
*pkg
= alpm_list_getdata(i
);
1038 char *string
= strdup(config
->print_format
);
1039 char *temp
= string
;
1041 if(strstr(temp
,"%n")) {
1042 string
= strreplace(temp
, "%n", alpm_pkg_get_name(pkg
));
1047 if(strstr(temp
,"%v")) {
1048 string
= strreplace(temp
, "%v", alpm_pkg_get_version(pkg
));
1053 if(strstr(temp
,"%l")) {
1054 char *pkgloc
= pkg_get_location(pkg
);
1055 string
= strreplace(temp
, "%l", pkgloc
);
1061 if(strstr(temp
,"%r")) {
1062 const char *repo
= "local";
1063 alpm_db_t
*db
= alpm_pkg_get_db(pkg
);
1065 repo
= alpm_db_get_name(db
);
1067 string
= strreplace(temp
, "%r", repo
);
1072 if(strstr(temp
,"%s")) {
1074 pm_asprintf(&size
, "%jd", (intmax_t)pkg_get_size(pkg
));
1075 string
= strreplace(temp
, "%s", size
);
1079 printf("%s\n",string
);
1084 /* Helper function for comparing strings using the
1085 * alpm "compare func" signature */
1086 int str_cmp(const void *s1
, const void *s2
)
1088 return strcmp(s1
, s2
);
1091 void display_new_optdepends(alpm_pkg_t
*oldpkg
, alpm_pkg_t
*newpkg
)
1093 alpm_list_t
*old
= alpm_pkg_get_optdepends(oldpkg
);
1094 alpm_list_t
*new = alpm_pkg_get_optdepends(newpkg
);
1095 alpm_list_t
*optdeps
= alpm_list_diff(new,old
,str_cmp
);
1097 printf(_("New optional dependencies for %s\n"), alpm_pkg_get_name(newpkg
));
1098 list_display_linebreak(" ", optdeps
);
1100 alpm_list_free(optdeps
);
1103 void display_optdepends(alpm_pkg_t
*pkg
)
1105 alpm_list_t
*optdeps
= alpm_pkg_get_optdepends(pkg
);
1107 printf(_("Optional dependencies for %s\n"), alpm_pkg_get_name(pkg
));
1108 list_display_linebreak(" ", optdeps
);
1112 static void display_repo_list(const char *dbname
, alpm_list_t
*list
)
1114 const char *prefix
= " ";
1117 printf(_("Repository %s\n"), dbname
);
1118 list_display(prefix
, list
);
1121 void select_display(const alpm_list_t
*pkglist
)
1123 const alpm_list_t
*i
;
1125 alpm_list_t
*list
= NULL
;
1126 char *string
= NULL
;
1127 const char *dbname
= NULL
;
1129 for (i
= pkglist
; i
; i
= i
->next
) {
1130 alpm_pkg_t
*pkg
= alpm_list_getdata(i
);
1131 alpm_db_t
*db
= alpm_pkg_get_db(pkg
);
1134 dbname
= alpm_db_get_name(db
);
1135 if(strcmp(alpm_db_get_name(db
), dbname
) != 0) {
1136 display_repo_list(dbname
, list
);
1138 dbname
= alpm_db_get_name(db
);
1141 pm_asprintf(&string
, "%d) %s", nth
, alpm_pkg_get_name(pkg
));
1142 list
= alpm_list_add(list
, string
);
1145 display_repo_list(dbname
, list
);
1149 static int parseindex(char *s
, int *val
, int min
, int max
)
1151 char *endptr
= NULL
;
1152 int n
= strtol(s
, &endptr
, 10);
1153 if(*endptr
== '\0') {
1154 if(n
< min
|| n
> max
) {
1155 fprintf(stderr
, _("Invalid value: %d is not between %d and %d\n"),
1162 fprintf(stderr
, _("Invalid number: %s\n"), s
);
1167 static int multiselect_parse(char *array
, int count
, char *response
)
1169 char *str
, *saveptr
;
1171 for (str
= response
; ; str
= NULL
) {
1175 char *starts
= strtok_r(str
, " ", &saveptr
);
1177 if(starts
== NULL
) {
1181 int len
= strlen(starts
);
1185 if(*starts
== '^') {
1190 /* if first token is including, we unselect all targets */
1191 memset(array
, 0, count
);
1195 /* check for range */
1197 if((p
= strchr(starts
+ 1, '-'))) {
1203 if(parseindex(starts
, &start
, 1, count
) != 0)
1207 array
[start
-1] = include
;
1210 if(parseindex(ends
, &end
, start
, count
) != 0) {
1213 for(d
= start
; d
<= end
; d
++) {
1214 array
[d
-1] = include
;
1222 int multiselect_question(char *array
, int count
)
1224 char *response
, *lastchar
;
1226 size_t response_len
= 64;
1228 if(config
->noconfirm
) {
1231 /* Use stderr so questions are always displayed when redirecting output */
1235 response
= malloc(response_len
);
1239 lastchar
= response
+ response_len
- 1;
1240 /* sentinel byte to later see if we filled up the entire string */
1244 memset(array
, 1, count
);
1246 fprintf(stream
, "\n");
1247 fprintf(stream
, _("Enter a selection (default=all)"));
1248 fprintf(stream
, ": ");
1251 if(config
->noconfirm
) {
1252 fprintf(stream
, "\n");
1258 if(fgets(response
, response_len
, stdin
)) {
1259 const size_t response_incr
= 64;
1260 /* handle buffer not being large enough to read full line case */
1261 while(*lastchar
== '\0' && lastchar
[-1] != '\n') {
1262 response_len
+= response_incr
;
1263 response
= realloc(response
, response_len
);
1267 lastchar
= response
+ response_len
- 1;
1270 if(fgets(response
+ response_len
- response_incr
- 1,
1271 response_incr
+ 1, stdin
) == 0) {
1277 if(strlen(response
) > 0) {
1278 if(multiselect_parse(array
, count
, response
) == -1) {
1279 /* only loop if user gave an invalid answer */
1294 int select_question(int count
)
1300 if(config
->noconfirm
) {
1303 /* Use stderr so questions are always displayed when redirecting output */
1308 fprintf(stream
, "\n");
1309 fprintf(stream
, _("Enter a number (default=%d)"), preset
);
1310 fprintf(stream
, ": ");
1312 if(config
->noconfirm
) {
1313 fprintf(stream
, "\n");
1319 if(fgets(response
, sizeof(response
), stdin
)) {
1321 if(strlen(response
) > 0) {
1323 if(parseindex(response
, &n
, 1, count
) != 0)
1331 return (preset
- 1);
1335 /* presents a prompt and gets a Y/N answer */
1336 static int question(short preset
, char *fmt
, va_list args
)
1341 if(config
->noconfirm
) {
1344 /* Use stderr so questions are always displayed when redirecting output */
1348 /* ensure all text makes it to the screen before we prompt the user */
1352 vfprintf(stream
, fmt
, args
);
1355 fprintf(stream
, " %s ", _("[Y/n]"));
1357 fprintf(stream
, " %s ", _("[y/N]"));
1360 if(config
->noconfirm
) {
1361 fprintf(stream
, "\n");
1368 if(fgets(response
, sizeof(response
), stdin
)) {
1370 if(strlen(response
) == 0) {
1374 if(strcasecmp(response
, _("Y")) == 0 || strcasecmp(response
, _("YES")) == 0) {
1376 } else if(strcasecmp(response
, _("N")) == 0 || strcasecmp(response
, _("NO")) == 0) {
1383 int yesno(char *fmt
, ...)
1388 va_start(args
, fmt
);
1389 ret
= question(1, fmt
, args
);
1395 int noyes(char *fmt
, ...)
1400 va_start(args
, fmt
);
1401 ret
= question(0, fmt
, args
);
1407 int pm_printf(alpm_loglevel_t level
, const char *format
, ...)
1412 /* print the message using va_arg list */
1413 va_start(args
, format
);
1414 ret
= pm_vfprintf(stdout
, level
, format
, args
);
1420 int pm_fprintf(FILE *stream
, alpm_loglevel_t level
, const char *format
, ...)
1425 /* print the message using va_arg list */
1426 va_start(args
, format
);
1427 ret
= pm_vfprintf(stream
, level
, format
, args
);
1433 int pm_asprintf(char **string
, const char *format
, ...)
1438 /* print the message using va_arg list */
1439 va_start(args
, format
);
1440 if(vasprintf(string
, format
, args
) == -1) {
1441 pm_fprintf(stderr
, ALPM_LOG_ERROR
, _("failed to allocate string\n"));
1449 int pm_vasprintf(char **string
, alpm_loglevel_t level
, const char *format
, va_list args
)
1454 /* if current logmask does not overlap with level, do not print msg */
1455 if(!(config
->logmask
& level
)) {
1459 /* print the message using va_arg list */
1460 ret
= vasprintf(&msg
, format
, args
);
1462 /* print a prefix to the message */
1464 case ALPM_LOG_ERROR
:
1465 pm_asprintf(string
, _("error: %s"), msg
);
1467 case ALPM_LOG_WARNING
:
1468 pm_asprintf(string
, _("warning: %s"), msg
);
1470 case ALPM_LOG_DEBUG
:
1471 pm_asprintf(string
, "debug: %s", msg
);
1473 case ALPM_LOG_FUNCTION
:
1474 pm_asprintf(string
, "function: %s", msg
);
1477 pm_asprintf(string
, "%s", msg
);
1485 int pm_vfprintf(FILE *stream
, alpm_loglevel_t level
, const char *format
, va_list args
)
1489 /* if current logmask does not overlap with level, do not print msg */
1490 if(!(config
->logmask
& level
)) {
1494 #if defined(PACMAN_DEBUG)
1495 /* If debug is on, we'll timestamp the output */
1496 if(config
->logmask
& ALPM_LOG_DEBUG
) {
1499 char timestr
[10] = {0};
1502 tmp
= localtime(&t
);
1503 strftime(timestr
, 9, "%H:%M:%S", tmp
);
1506 fprintf(stream
, "[%s] ", timestr
);
1510 /* print a prefix to the message */
1512 case ALPM_LOG_ERROR
:
1513 fprintf(stream
, _("error: "));
1515 case ALPM_LOG_WARNING
:
1516 fprintf(stream
, _("warning: "));
1518 case ALPM_LOG_DEBUG
:
1519 fprintf(stream
, "debug: ");
1521 case ALPM_LOG_FUNCTION
:
1522 fprintf(stream
, "function: ");
1528 /* print the message using va_arg list */
1529 ret
= vfprintf(stream
, format
, args
);
1533 #ifndef HAVE_STRNDUP
1534 /* A quick and dirty implementation derived from glibc */
1535 static size_t strnlen(const char *s
, size_t max
)
1537 register const char *p
;
1538 for(p
= s
; *p
&& max
--; ++p
);
1542 char *strndup(const char *s
, size_t n
)
1544 size_t len
= strnlen(s
, n
);
1545 char *new = (char *) malloc(len
+ 1);
1551 return (char *)memcpy(new, s
, len
);
1555 /* vim: set ts=2 sw=2 noet: */