4 * Copyright (c) 2006-2012 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 <sys/types.h>
22 #include <sys/ioctl.h>
29 #include <stdint.h> /* intmax_t */
38 #include <termios.h> /* tcflush */
42 #include <alpm_list.h>
50 int trans_init(alpm_transflag_t flags
, int check_valid
)
54 check_syncdbs(0, check_valid
);
56 ret
= alpm_trans_init(config
->handle
, flags
);
64 void trans_init_error(void)
66 alpm_errno_t err
= alpm_errno(config
->handle
);
67 pm_printf(ALPM_LOG_ERROR
, _("failed to init transaction (%s)\n"),
69 if(err
== ALPM_ERR_HANDLE_LOCK
) {
70 const char *lockfile
= alpm_option_get_lockfile(config
->handle
);
71 pm_printf(ALPM_LOG_ERROR
, _("could not lock database: %s\n"),
73 if(access(lockfile
, F_OK
) == 0) {
74 fprintf(stderr
, _(" if you're sure a package manager is not already\n"
75 " running, you can remove %s\n"), lockfile
);
80 int trans_release(void)
82 if(alpm_trans_release(config
->handle
) == -1) {
83 pm_printf(ALPM_LOG_ERROR
, _("failed to release transaction (%s)\n"),
84 alpm_strerror(alpm_errno(config
->handle
)));
97 return !config
->print
;
99 return (config
->op_s_clean
|| config
->op_s_sync
||
100 (!config
->group
&& !config
->op_s_info
&& !config
->op_q_list
&&
101 !config
->op_s_search
&& !config
->print
));
107 int check_syncdbs(size_t need_repos
, int check_valid
)
111 alpm_list_t
*sync_dbs
= alpm_get_syncdbs(config
->handle
);
113 if(need_repos
&& sync_dbs
== NULL
) {
114 pm_printf(ALPM_LOG_ERROR
, _("no usable package repositories configured.\n"));
119 /* ensure all known dbs are valid */
120 for(i
= sync_dbs
; i
; i
= alpm_list_next(i
)) {
121 alpm_db_t
*db
= i
->data
;
122 if(alpm_db_get_valid(db
)) {
123 pm_printf(ALPM_LOG_ERROR
, _("database '%s' is not valid (%s)\n"),
124 alpm_db_get_name(db
), alpm_strerror(alpm_errno(config
->handle
)));
132 /* discard unhandled input on the terminal's input buffer */
133 static int flush_term_input(int fd
) {
136 return tcflush(fd
, TCIFLUSH
);
144 /* gets the current screen column width */
145 unsigned short getcols(int fd
)
147 const unsigned short default_tty
= 80;
148 const unsigned short default_notty
= 0;
149 unsigned short termwidth
= 0;
152 return default_notty
;
155 #if defined(TIOCGSIZE)
157 if(ioctl(fd
, TIOCGSIZE
, &win
) == 0) {
158 termwidth
= win
.ts_cols
;
160 #elif defined(TIOCGWINSZ)
162 if(ioctl(fd
, TIOCGWINSZ
, &win
) == 0) {
163 termwidth
= win
.ws_col
;
166 return termwidth
== 0 ? default_tty
: termwidth
;
169 /* does the same thing as 'rm -rf' */
170 int rmrf(const char *path
)
179 if(errno
== ENOENT
) {
181 } else if(errno
== EPERM
) {
183 } else if(errno
== EISDIR
) {
185 } else if(errno
== ENOTDIR
) {
188 /* not a directory */
192 dirp
= opendir(path
);
196 for(dp
= readdir(dirp
); dp
!= NULL
; dp
= readdir(dirp
)) {
198 if(strcmp(dp
->d_name
, "..") != 0 && strcmp(dp
->d_name
, ".") != 0) {
200 snprintf(name
, PATH_MAX
, "%s/%s", path
, dp
->d_name
);
201 errflag
+= rmrf(name
);
213 /** Parse the basename of a program from a path.
214 * @param path path to parse basename from
216 * @return everything following the final '/'
218 const char *mbasename(const char *path
)
220 const char *last
= strrchr(path
, '/');
227 /** Parse the dirname of a program from a path.
228 * The path returned should be freed.
229 * @param path path to parse dirname from
231 * @return everything preceding the final '/'
233 char *mdirname(const char *path
)
237 /* null or empty path */
238 if(path
== NULL
|| path
== '\0') {
243 last
= strrchr(ret
, '/');
246 /* we found a '/', so terminate our string */
255 /* output a string, but wrap words properly with a specified indentation
257 void indentprint(const char *str
, unsigned short indent
, unsigned short cols
)
267 /* if we're not a tty, or our tty is not wide enough that wrapping even makes
268 * sense, print without indenting */
269 if(cols
== 0 || indent
> cols
) {
274 len
= strlen(str
) + 1;
275 wcstr
= calloc(len
, sizeof(wchar_t));
276 len
= mbstowcs(wcstr
, str
, len
);
286 const wchar_t *q
, *next
;
288 if(p
== NULL
|| *p
== L
' ') continue;
289 next
= wcschr(p
, L
' ');
291 next
= p
+ wcslen(p
);
293 /* len captures # cols */
297 len
+= wcwidth(*q
++);
299 if(len
> (cols
- cidx
- 1)) {
300 /* wrap to a newline and reindent */
301 printf("\n%-*s", (int)indent
, "");
309 printf("%lc", (wint_t)*p
);
316 /* Trim whitespace and newlines from a string
318 size_t strtrim(char *str
)
320 char *end
, *pch
= str
;
322 if(str
== NULL
|| *str
== '\0') {
323 /* string is empty, so we're done. */
327 while(isspace((unsigned char)*pch
)) {
331 size_t len
= strlen(pch
);
333 memmove(str
, pch
, len
+ 1);
339 /* check if there wasn't anything but whitespace in the string. */
344 end
= (str
+ strlen(str
) - 1);
345 while(isspace((unsigned char)*end
)) {
353 /* Replace all occurances of 'needle' with 'replace' in 'str', returning
354 * a new string (must be free'd) */
355 char *strreplace(const char *str
, const char *needle
, const char *replace
)
357 const char *p
= NULL
, *q
= NULL
;
358 char *newstr
= NULL
, *newp
= NULL
;
359 alpm_list_t
*i
= NULL
, *list
= NULL
;
360 size_t needlesz
= strlen(needle
), replacesz
= strlen(replace
);
368 q
= strstr(p
, needle
);
370 list
= alpm_list_add(list
, (char *)q
);
372 q
= strstr(p
, needle
);
375 /* no occurences of needle found */
379 /* size of new string = size of old string + "number of occurences of needle"
380 * x "size difference between replace and needle" */
381 newsz
= strlen(str
) + 1 +
382 alpm_list_count(list
) * (replacesz
- needlesz
);
383 newstr
= calloc(newsz
, sizeof(char));
390 for(i
= list
; i
; i
= alpm_list_next(i
)) {
393 /* add chars between this occurence and last occurence, if any */
394 memcpy(newp
, p
, (size_t)(q
- p
));
397 memcpy(newp
, replace
, replacesz
);
401 alpm_list_free(list
);
404 /* add the rest of 'p' */
411 /** Splits a string into a list of strings using the chosen character as
414 * @param str the string to split
415 * @param splitchar the character to split at
417 * @return a list containing the duplicated strings
419 alpm_list_t
*strsplit(const char *str
, const char splitchar
)
421 alpm_list_t
*list
= NULL
;
422 const char *prev
= str
;
425 while((str
= strchr(str
, splitchar
))) {
426 dup
= strndup(prev
, (size_t)(str
- prev
));
430 list
= alpm_list_add(list
, dup
);
440 list
= alpm_list_add(list
, dup
);
445 static size_t string_length(const char *s
)
450 if(!s
|| s
[0] == '\0') {
453 /* len goes from # bytes -> # chars -> # cols */
455 wcstr
= calloc(len
, sizeof(wchar_t));
456 len
= mbstowcs(wcstr
, s
, len
);
457 len
= wcswidth(wcstr
, len
);
463 void string_display(const char *title
, const char *string
, unsigned short cols
)
466 printf("%s ", title
);
468 if(string
== NULL
|| string
[0] == '\0') {
471 /* compute the length of title + a space */
472 size_t len
= string_length(title
) + 1;
473 indentprint(string
, (unsigned short)len
, cols
);
478 static void table_print_line(const alpm_list_t
*line
, short col_padding
,
479 size_t colcount
, size_t *widths
, int *has_data
)
481 size_t i
, lastcol
= 0;
482 int need_padding
= 0;
483 const alpm_list_t
*curcell
;
485 for(i
= colcount
; i
> 0; i
--) {
486 if(has_data
[i
- 1]) {
492 for(i
= 0, curcell
= line
; curcell
&& i
< colcount
;
493 i
++, curcell
= alpm_list_next(curcell
)) {
501 value
= curcell
->data
;
505 /* silly printf requires padding size to be an int */
506 cell_padding
= (int)widths
[i
] - (int)string_length(value
);
507 if(cell_padding
< 0) {
511 printf("%*s", col_padding
, "");
513 /* left-align all but the last column */
515 printf("%s%*s", value
, cell_padding
, "");
517 printf("%*s%s", cell_padding
, "", value
);
528 * Find the max string width of each column. Also determines whether values
529 * exist in the column and sets the value in has_data accordingly.
530 * @param header a list of header strings
531 * @param rows a list of lists of rows as strings
532 * @param padding the amount of padding between columns
533 * @param totalcols the total number of columns in the header and each row
534 * @param widths a pointer to store width data
535 * @param has_data a pointer to store whether column has data
537 * @return the total width of the table; 0 on failure
539 static size_t table_calc_widths(const alpm_list_t
*header
,
540 const alpm_list_t
*rows
, short padding
, size_t totalcols
,
541 size_t **widths
, int **has_data
)
543 const alpm_list_t
*i
;
544 size_t curcol
, totalwidth
= 0, usefulcols
= 0;
552 colwidths
= malloc(totalcols
* sizeof(size_t));
553 coldata
= calloc(totalcols
, sizeof(int));
554 if(!colwidths
|| !coldata
) {
557 /* header determines column count and initial values of longest_strs */
558 for(i
= header
, curcol
= 0; i
; i
= alpm_list_next(i
), curcol
++) {
559 colwidths
[curcol
] = string_length(i
->data
);
560 /* note: header does not determine whether column has data */
563 /* now find the longest string in each column */
564 for(i
= rows
; i
; i
= alpm_list_next(i
)) {
565 /* grab first column of each row and iterate through columns */
566 const alpm_list_t
*j
= i
->data
;
567 for(curcol
= 0; j
; j
= alpm_list_next(j
), curcol
++) {
568 const char *str
= j
->data
;
569 size_t str_len
= string_length(str
);
571 if(str_len
> colwidths
[curcol
]) {
572 colwidths
[curcol
] = str_len
;
580 for(i
= header
, curcol
= 0; i
; i
= alpm_list_next(i
), curcol
++) {
581 /* only include columns that have data */
582 if(coldata
[curcol
]) {
584 totalwidth
+= colwidths
[curcol
];
588 /* add padding between columns */
590 totalwidth
+= padding
* (usefulcols
- 1);
598 /** Displays the list in table format
600 * @param title the tables title
601 * @param header the column headers. column count is determined by the nr
603 * @param rows the rows to display as a list of lists of strings. the outer
604 * list represents the rows, the inner list the cells (= columns)
605 * @param cols the number of columns available in the terminal
606 * @return -1 if not enough terminal cols available, else 0
608 static int table_display(const char *title
, const alpm_list_t
*header
,
609 const alpm_list_t
*rows
, unsigned short cols
)
611 const unsigned short padding
= 2;
612 const alpm_list_t
*i
;
613 size_t *widths
= NULL
, totalcols
, totalwidth
;
614 int *has_data
= NULL
;
616 if(rows
== NULL
|| header
== NULL
) {
620 totalcols
= alpm_list_count(header
);
621 totalwidth
= table_calc_widths(header
, rows
, padding
, totalcols
,
623 /* return -1 if terminal is not wide enough */
624 if(totalwidth
> cols
) {
625 pm_printf(ALPM_LOG_WARNING
,
626 _("insufficient columns available for table display\n"));
629 if(!totalwidth
|| !widths
|| !has_data
) {
634 printf("%s\n\n", title
);
637 table_print_line(header
, padding
, totalcols
, widths
, has_data
);
640 for(i
= rows
; i
; i
= alpm_list_next(i
)) {
641 table_print_line(i
->data
, padding
, totalcols
, widths
, has_data
);
649 void list_display(const char *title
, const alpm_list_t
*list
,
650 unsigned short maxcols
)
652 const alpm_list_t
*i
;
656 len
= string_length(title
) + 1;
657 printf("%s ", title
);
661 printf("%s\n", _("None"));
664 const char *str
= list
->data
;
666 cols
+= string_length(str
);
667 for(i
= alpm_list_next(list
); i
; i
= alpm_list_next(i
)) {
669 size_t s
= string_length(str
);
670 /* wrap only if we have enough usable column space */
671 if(maxcols
> len
&& cols
+ s
+ 2 >= maxcols
) {
675 for(j
= 1; j
<= len
; j
++) {
678 } else if(cols
!= len
) {
679 /* 2 spaces are added if this is not the first element on a line. */
690 void list_display_linebreak(const char *title
, const alpm_list_t
*list
,
691 unsigned short maxcols
)
693 unsigned short len
= 0;
696 len
= (unsigned short)string_length(title
) + 1;
697 printf("%s ", title
);
701 printf("%s\n", _("None"));
703 const alpm_list_t
*i
;
704 /* Print the first element */
705 indentprint((const char *)list
->data
, len
, maxcols
);
708 for(i
= alpm_list_next(list
); i
; i
= alpm_list_next(i
)) {
710 for(j
= 1; j
<= len
; j
++) {
713 indentprint((const char *)i
->data
, len
, maxcols
);
719 void signature_display(const char *title
, alpm_siglist_t
*siglist
,
720 unsigned short maxcols
)
722 unsigned short len
= 0;
725 len
= (unsigned short)string_length(title
) + 1;
726 printf("%s ", title
);
728 if(siglist
->count
== 0) {
732 for(i
= 0; i
< siglist
->count
; i
++) {
734 const char *status
, *validity
, *name
;
736 alpm_sigresult_t
*result
= siglist
->results
+ i
;
737 /* Don't re-indent the first result */
740 for(j
= 1; j
<= len
; j
++) {
744 switch(result
->status
) {
745 case ALPM_SIGSTATUS_VALID
:
748 case ALPM_SIGSTATUS_KEY_EXPIRED
:
749 status
= _("Key expired");
751 case ALPM_SIGSTATUS_SIG_EXPIRED
:
752 status
= _("Expired");
754 case ALPM_SIGSTATUS_INVALID
:
755 status
= _("Invalid");
757 case ALPM_SIGSTATUS_KEY_UNKNOWN
:
758 status
= _("Key unknown");
760 case ALPM_SIGSTATUS_KEY_DISABLED
:
761 status
= _("Key disabled");
764 status
= _("Signature error");
767 switch(result
->validity
) {
768 case ALPM_SIGVALIDITY_FULL
:
769 validity
= _("full trust");
771 case ALPM_SIGVALIDITY_MARGINAL
:
772 validity
= _("marginal trust");
774 case ALPM_SIGVALIDITY_NEVER
:
775 validity
= _("never trust");
777 case ALPM_SIGVALIDITY_UNKNOWN
:
779 validity
= _("unknown trust");
782 name
= result
->key
.uid
? result
->key
.uid
: result
->key
.fingerprint
;
783 ret
= pm_asprintf(&sigline
, _("%s, %s from \"%s\""),
784 status
, validity
, name
);
786 pm_printf(ALPM_LOG_ERROR
, _("failed to allocate string\n"));
789 indentprint(sigline
, len
, maxcols
);
796 /* creates a header row for use with table_display */
797 static alpm_list_t
*create_verbose_header(void)
799 alpm_list_t
*res
= NULL
;
803 res
= alpm_list_add(res
, str
);
804 str
= _("Old Version");
805 res
= alpm_list_add(res
, str
);
806 str
= _("New Version");
807 res
= alpm_list_add(res
, str
);
808 str
= _("Net Change");
809 res
= alpm_list_add(res
, str
);
810 str
= _("Download Size");
811 res
= alpm_list_add(res
, str
);
816 /* returns package info as list of strings */
817 static alpm_list_t
*create_verbose_row(pm_target_t
*target
)
823 alpm_list_t
*ret
= NULL
;
825 /* a row consists of the package name, */
826 if(target
->install
) {
827 const alpm_db_t
*db
= alpm_pkg_get_db(target
->install
);
829 pm_asprintf(&str
, "%s/%s", alpm_db_get_name(db
), alpm_pkg_get_name(target
->install
));
831 pm_asprintf(&str
, "%s", alpm_pkg_get_name(target
->install
));
834 pm_asprintf(&str
, "%s", alpm_pkg_get_name(target
->remove
));
836 ret
= alpm_list_add(ret
, str
);
838 /* old and new versions */
839 pm_asprintf(&str
, "%s",
840 target
->remove
!= NULL
? alpm_pkg_get_version(target
->remove
) : "");
841 ret
= alpm_list_add(ret
, str
);
843 pm_asprintf(&str
, "%s",
844 target
->install
!= NULL
? alpm_pkg_get_version(target
->install
) : "");
845 ret
= alpm_list_add(ret
, str
);
848 size
-= target
->remove
? alpm_pkg_get_isize(target
->remove
) : 0;
849 size
+= target
->install
? alpm_pkg_get_isize(target
->install
) : 0;
850 human_size
= humanize_size(size
, 'M', 2, &label
);
851 pm_asprintf(&str
, "%.2f %s", human_size
, label
);
852 ret
= alpm_list_add(ret
, str
);
854 size
= target
->install
? alpm_pkg_download_size(target
->install
) : 0;
856 human_size
= humanize_size(size
, 'M', 2, &label
);
857 pm_asprintf(&str
, "%.2f %s", human_size
, label
);
861 ret
= alpm_list_add(ret
, str
);
866 /* prepare a list of pkgs to display */
867 static void _display_targets(alpm_list_t
*targets
, int verbose
)
872 off_t isize
= 0, rsize
= 0, dlsize
= 0;
874 alpm_list_t
*i
, *rows
= NULL
, *names
= NULL
;
880 /* gather package info */
881 for(i
= targets
; i
; i
= alpm_list_next(i
)) {
882 pm_target_t
*target
= i
->data
;
884 if(target
->install
) {
885 dlsize
+= alpm_pkg_download_size(target
->install
);
886 isize
+= alpm_pkg_get_isize(target
->install
);
889 /* add up size of all removed packages */
890 rsize
+= alpm_pkg_get_isize(target
->remove
);
894 /* form data for both verbose and non-verbose display */
895 for(i
= targets
; i
; i
= alpm_list_next(i
)) {
896 pm_target_t
*target
= i
->data
;
898 rows
= alpm_list_add(rows
, create_verbose_row(target
));
899 if(target
->install
) {
900 pm_asprintf(&str
, "%s-%s", alpm_pkg_get_name(target
->install
),
901 alpm_pkg_get_version(target
->install
));
902 } else if(isize
== 0) {
903 pm_asprintf(&str
, "%s-%s", alpm_pkg_get_name(target
->remove
),
904 alpm_pkg_get_version(target
->remove
));
906 pm_asprintf(&str
, "%s-%s [removal]", alpm_pkg_get_name(target
->remove
),
907 alpm_pkg_get_version(target
->remove
));
909 names
= alpm_list_add(names
, str
);
912 /* print to screen */
913 pm_asprintf(&str
, _("Packages (%d):"), alpm_list_count(targets
));
916 cols
= getcols(fileno(stdout
));
918 alpm_list_t
*header
= create_verbose_header();
919 if(table_display(str
, header
, rows
, cols
) != 0) {
920 /* fallback to list display if table wouldn't fit */
921 list_display(str
, names
, cols
);
923 alpm_list_free(header
);
925 list_display(str
, names
, cols
);
929 /* rows is a list of lists of strings, free inner lists here */
930 for(i
= rows
; i
; i
= alpm_list_next(i
)) {
931 alpm_list_t
*lp
= i
->data
;
934 alpm_list_free(rows
);
938 if(dlsize
> 0 || config
->op_s_downloadonly
) {
939 size
= humanize_size(dlsize
, 'M', 2, &label
);
940 printf(_("Total Download Size: %.2f %s\n"), size
, label
);
942 if(!config
->op_s_downloadonly
) {
944 size
= humanize_size(isize
, 'M', 2, &label
);
945 printf(_("Total Installed Size: %.2f %s\n"), size
, label
);
947 if(rsize
> 0 && isize
== 0) {
948 size
= humanize_size(rsize
, 'M', 2, &label
);
949 printf(_("Total Removed Size: %.2f %s\n"), size
, label
);
951 /* only show this net value if different from raw installed size */
952 if(isize
> 0 && rsize
> 0) {
953 size
= humanize_size(isize
- rsize
, 'M', 2, &label
);
954 printf(_("Net Upgrade Size: %.2f %s\n"), size
, label
);
959 static int target_cmp(const void *p1
, const void *p2
)
961 const pm_target_t
*targ1
= p1
;
962 const pm_target_t
*targ2
= p2
;
963 /* explicit are always sorted after implicit (e.g. deps, pulled targets) */
964 if(targ1
->is_explicit
!= targ2
->is_explicit
) {
965 return targ1
->is_explicit
> targ2
->is_explicit
;
967 const char *name1
= targ1
->install
?
968 alpm_pkg_get_name(targ1
->install
) : alpm_pkg_get_name(targ1
->remove
);
969 const char *name2
= targ2
->install
?
970 alpm_pkg_get_name(targ2
->install
) : alpm_pkg_get_name(targ2
->remove
);
971 return strcmp(name1
, name2
);
974 static int pkg_cmp(const void *p1
, const void *p2
)
976 /* explicit cast due to (un)necessary removal of const */
977 alpm_pkg_t
*pkg1
= (alpm_pkg_t
*)p1
;
978 alpm_pkg_t
*pkg2
= (alpm_pkg_t
*)p2
;
979 return strcmp(alpm_pkg_get_name(pkg1
), alpm_pkg_get_name(pkg2
));
982 void display_targets(void)
984 alpm_list_t
*i
, *targets
= NULL
;
985 alpm_db_t
*db_local
= alpm_get_localdb(config
->handle
);
987 for(i
= alpm_trans_get_add(config
->handle
); i
; i
= alpm_list_next(i
)) {
988 alpm_pkg_t
*pkg
= i
->data
;
989 pm_target_t
*targ
= calloc(1, sizeof(pm_target_t
));
992 targ
->remove
= alpm_db_get_pkg(db_local
, alpm_pkg_get_name(pkg
));
993 if(alpm_list_find(config
->explicit_adds
, pkg
, pkg_cmp
)) {
994 targ
->is_explicit
= 1;
996 targets
= alpm_list_add(targets
, targ
);
998 for(i
= alpm_trans_get_remove(config
->handle
); i
; i
= alpm_list_next(i
)) {
999 alpm_pkg_t
*pkg
= i
->data
;
1000 pm_target_t
*targ
= calloc(1, sizeof(pm_target_t
));
1003 if(alpm_list_find(config
->explicit_removes
, pkg
, pkg_cmp
)) {
1004 targ
->is_explicit
= 1;
1006 targets
= alpm_list_add(targets
, targ
);
1009 targets
= alpm_list_msort(targets
, alpm_list_count(targets
), target_cmp
);
1010 _display_targets(targets
, config
->verbosepkglists
);
1014 static off_t
pkg_get_size(alpm_pkg_t
*pkg
)
1016 switch(config
->op
) {
1018 return alpm_pkg_download_size(pkg
);
1020 return alpm_pkg_get_size(pkg
);
1022 return alpm_pkg_get_isize(pkg
);
1026 static char *pkg_get_location(alpm_pkg_t
*pkg
)
1028 alpm_list_t
*servers
;
1029 char *string
= NULL
;
1030 switch(config
->op
) {
1032 servers
= alpm_db_get_servers(alpm_pkg_get_db(pkg
));
1034 pm_asprintf(&string
, "%s/%s", servers
->data
,
1035 alpm_pkg_get_filename(pkg
));
1039 return strdup(alpm_pkg_get_filename(pkg
));
1041 pm_asprintf(&string
, "%s-%s", alpm_pkg_get_name(pkg
), alpm_pkg_get_version(pkg
));
1046 /* a pow() implementation that is specialized for an integer base and small,
1047 * positive-only integer exponents. */
1048 static double simple_pow(int base
, int exp
)
1050 double result
= 1.0;
1051 for(; exp
> 0; exp
--) {
1057 /** Converts sizes in bytes into human readable units.
1059 * @param bytes the size in bytes
1060 * @param target_unit '\0' or a short label. If equal to one of the short unit
1061 * labels ('B', 'K', ...) bytes is converted to target_unit; if '\0', the first
1062 * unit which will bring the value to below a threshold of 2048 will be chosen.
1063 * @param precision number of decimal places, ensures -0.00 gets rounded to
1064 * 0.00; -1 if no rounding desired
1065 * @param label will be set to the appropriate unit label
1067 * @return the size in the appropriate unit
1069 double humanize_size(off_t bytes
, const char target_unit
, int precision
,
1072 static const char *labels
[] = {"B", "KiB", "MiB", "GiB",
1073 "TiB", "PiB", "EiB", "ZiB", "YiB"};
1074 static const int unitcount
= sizeof(labels
) / sizeof(labels
[0]);
1076 double val
= (double)bytes
;
1079 for(index
= 0; index
< unitcount
- 1; index
++) {
1080 if(target_unit
!= '\0' && labels
[index
][0] == target_unit
) {
1082 } else if(target_unit
== '\0' && val
<= 2048.0 && val
>= -2048.0) {
1089 *label
= labels
[index
];
1092 /* fix FS#27924 so that it doesn't display negative zeroes */
1093 if(precision
>= 0 && val
< 0.0 &&
1094 val
> (-0.5 / simple_pow(10, precision
))) {
1101 void print_packages(const alpm_list_t
*packages
)
1103 const alpm_list_t
*i
;
1104 if(!config
->print_format
) {
1105 config
->print_format
= strdup("%l");
1107 for(i
= packages
; i
; i
= alpm_list_next(i
)) {
1108 alpm_pkg_t
*pkg
= i
->data
;
1109 char *string
= strdup(config
->print_format
);
1110 char *temp
= string
;
1112 if(strstr(temp
, "%n")) {
1113 string
= strreplace(temp
, "%n", alpm_pkg_get_name(pkg
));
1118 if(strstr(temp
, "%v")) {
1119 string
= strreplace(temp
, "%v", alpm_pkg_get_version(pkg
));
1124 if(strstr(temp
, "%l")) {
1125 char *pkgloc
= pkg_get_location(pkg
);
1126 string
= strreplace(temp
, "%l", pkgloc
);
1132 if(strstr(temp
, "%r")) {
1133 const char *repo
= "local";
1134 alpm_db_t
*db
= alpm_pkg_get_db(pkg
);
1136 repo
= alpm_db_get_name(db
);
1138 string
= strreplace(temp
, "%r", repo
);
1143 if(strstr(temp
, "%s")) {
1145 pm_asprintf(&size
, "%jd", (intmax_t)pkg_get_size(pkg
));
1146 string
= strreplace(temp
, "%s", size
);
1150 printf("%s\n", string
);
1156 * Helper function for comparing depends using the alpm "compare func"
1157 * signature. The function descends through the structure in the following
1158 * comparison order: name, modifier (e.g., '>', '='), version, description.
1159 * @param d1 the first depend structure
1160 * @param d2 the second depend structure
1161 * @return -1, 0, or 1 if first is <, ==, or > second
1163 static int depend_cmp(const void *d1
, const void *d2
)
1165 const alpm_depend_t
*dep1
= d1
;
1166 const alpm_depend_t
*dep2
= d2
;
1169 ret
= strcmp(dep1
->name
, dep2
->name
);
1171 ret
= dep1
->mod
- dep2
->mod
;
1174 if(dep1
->version
&& dep2
->version
) {
1175 ret
= strcmp(dep1
->version
, dep2
->version
);
1176 } else if(!dep1
->version
&& dep2
->version
) {
1178 } else if(dep1
->version
&& !dep2
->version
) {
1183 if(dep1
->desc
&& dep2
->desc
) {
1184 ret
= strcmp(dep1
->desc
, dep2
->desc
);
1185 } else if(!dep1
->desc
&& dep2
->desc
) {
1187 } else if(dep1
->desc
&& !dep2
->desc
) {
1195 void display_new_optdepends(alpm_pkg_t
*oldpkg
, alpm_pkg_t
*newpkg
)
1197 alpm_list_t
*i
, *old
, *new, *optdeps
, *optstrings
= NULL
;
1199 old
= alpm_pkg_get_optdepends(oldpkg
);
1200 new = alpm_pkg_get_optdepends(newpkg
);
1201 optdeps
= alpm_list_diff(new, old
, depend_cmp
);
1203 /* turn optdepends list into a text list */
1204 for(i
= optdeps
; i
; i
= alpm_list_next(i
)) {
1205 alpm_depend_t
*optdep
= i
->data
;
1206 optstrings
= alpm_list_add(optstrings
, alpm_dep_compute_string(optdep
));
1210 printf(_("New optional dependencies for %s\n"), alpm_pkg_get_name(newpkg
));
1211 unsigned short cols
= getcols(fileno(stdout
));
1212 list_display_linebreak(" ", optstrings
, cols
);
1215 alpm_list_free(optdeps
);
1216 FREELIST(optstrings
);
1219 void display_optdepends(alpm_pkg_t
*pkg
)
1221 alpm_list_t
*i
, *optdeps
, *optstrings
= NULL
;
1223 optdeps
= alpm_pkg_get_optdepends(pkg
);
1225 /* turn optdepends list into a text list */
1226 for(i
= optdeps
; i
; i
= alpm_list_next(i
)) {
1227 alpm_depend_t
*optdep
= i
->data
;
1228 optstrings
= alpm_list_add(optstrings
, alpm_dep_compute_string(optdep
));
1232 printf(_("Optional dependencies for %s\n"), alpm_pkg_get_name(pkg
));
1233 unsigned short cols
= getcols(fileno(stdout
));
1234 list_display_linebreak(" ", optstrings
, cols
);
1237 FREELIST(optstrings
);
1240 static void display_repo_list(const char *dbname
, alpm_list_t
*list
,
1241 unsigned short cols
)
1243 const char *prefix
= " ";
1246 printf(_("Repository %s\n"), dbname
);
1247 list_display(prefix
, list
, cols
);
1250 void select_display(const alpm_list_t
*pkglist
)
1252 const alpm_list_t
*i
;
1254 alpm_list_t
*list
= NULL
;
1255 char *string
= NULL
;
1256 const char *dbname
= NULL
;
1257 unsigned short cols
= getcols(fileno(stdout
));
1259 for(i
= pkglist
; i
; i
= i
->next
) {
1260 alpm_pkg_t
*pkg
= i
->data
;
1261 alpm_db_t
*db
= alpm_pkg_get_db(pkg
);
1264 dbname
= alpm_db_get_name(db
);
1265 if(strcmp(alpm_db_get_name(db
), dbname
) != 0) {
1266 display_repo_list(dbname
, list
, cols
);
1268 dbname
= alpm_db_get_name(db
);
1271 pm_asprintf(&string
, "%d) %s", nth
, alpm_pkg_get_name(pkg
));
1272 list
= alpm_list_add(list
, string
);
1275 display_repo_list(dbname
, list
, cols
);
1279 static int parseindex(char *s
, int *val
, int min
, int max
)
1281 char *endptr
= NULL
;
1282 int n
= strtol(s
, &endptr
, 10);
1283 if(*endptr
== '\0') {
1284 if(n
< min
|| n
> max
) {
1285 pm_printf(ALPM_LOG_ERROR
,
1286 _("invalid value: %d is not between %d and %d\n"),
1293 pm_printf(ALPM_LOG_ERROR
, _("invalid number: %s\n"), s
);
1298 static int multiselect_parse(char *array
, int count
, char *response
)
1300 char *str
, *saveptr
;
1302 for(str
= response
; ; str
= NULL
) {
1307 char *starts
= strtok_r(str
, " ,", &saveptr
);
1309 if(starts
== NULL
) {
1312 len
= strtrim(starts
);
1316 if(*starts
== '^') {
1321 /* if first token is including, we unselect all targets */
1322 memset(array
, 0, count
);
1326 /* check for range */
1328 if((p
= strchr(starts
+ 1, '-'))) {
1334 if(parseindex(starts
, &start
, 1, count
) != 0)
1338 array
[start
-1] = include
;
1341 if(parseindex(ends
, &end
, start
, count
) != 0) {
1344 for(d
= start
; d
<= end
; d
++) {
1345 array
[d
-1] = include
;
1353 int multiselect_question(char *array
, int count
)
1355 char *response
, *lastchar
;
1357 size_t response_len
= 64;
1359 if(config
->noconfirm
) {
1362 /* Use stderr so questions are always displayed when redirecting output */
1366 response
= malloc(response_len
);
1370 lastchar
= response
+ response_len
- 1;
1371 /* sentinel byte to later see if we filled up the entire string */
1375 memset(array
, 1, count
);
1377 fprintf(stream
, "\n");
1378 fprintf(stream
, _("Enter a selection (default=all)"));
1379 fprintf(stream
, ": ");
1382 if(config
->noconfirm
) {
1383 fprintf(stream
, "\n");
1387 flush_term_input(fileno(stdin
));
1389 if(fgets(response
, response_len
, stdin
)) {
1390 const size_t response_incr
= 64;
1392 /* handle buffer not being large enough to read full line case */
1393 while(*lastchar
== '\0' && lastchar
[-1] != '\n') {
1394 response_len
+= response_incr
;
1395 response
= realloc(response
, response_len
);
1399 lastchar
= response
+ response_len
- 1;
1402 if(fgets(response
+ response_len
- response_incr
- 1,
1403 response_incr
+ 1, stdin
) == 0) {
1409 len
= strtrim(response
);
1411 if(multiselect_parse(array
, count
, response
) == -1) {
1412 /* only loop if user gave an invalid answer */
1427 int select_question(int count
)
1433 if(config
->noconfirm
) {
1436 /* Use stderr so questions are always displayed when redirecting output */
1441 fprintf(stream
, "\n");
1442 fprintf(stream
, _("Enter a number (default=%d)"), preset
);
1443 fprintf(stream
, ": ");
1445 if(config
->noconfirm
) {
1446 fprintf(stream
, "\n");
1450 flush_term_input(fileno(stdin
));
1452 if(fgets(response
, sizeof(response
), stdin
)) {
1453 size_t len
= strtrim(response
);
1456 if(parseindex(response
, &n
, 1, count
) != 0)
1464 return (preset
- 1);
1468 /* presents a prompt and gets a Y/N answer */
1469 static int question(short preset
, char *fmt
, va_list args
)
1473 int fd_in
= fileno(stdin
);
1475 if(config
->noconfirm
) {
1478 /* Use stderr so questions are always displayed when redirecting output */
1482 /* ensure all text makes it to the screen before we prompt the user */
1486 vfprintf(stream
, fmt
, args
);
1489 fprintf(stream
, " %s ", _("[Y/n]"));
1491 fprintf(stream
, " %s ", _("[y/N]"));
1494 if(config
->noconfirm
) {
1495 fprintf(stream
, "\n");
1500 flush_term_input(fd_in
);
1502 if(fgets(response
, sizeof(response
), stdin
)) {
1503 size_t len
= strtrim(response
);
1508 /* if stdin is piped, response does not get printed out, and as a result
1509 * a \n is missing, resulting in broken output (FS#27909) */
1510 if(!isatty(fd_in
)) {
1511 fprintf(stream
, "%s\n", response
);
1514 if(strcasecmp(response
, _("Y")) == 0 || strcasecmp(response
, _("YES")) == 0) {
1516 } else if(strcasecmp(response
, _("N")) == 0 || strcasecmp(response
, _("NO")) == 0) {
1523 int yesno(char *fmt
, ...)
1528 va_start(args
, fmt
);
1529 ret
= question(1, fmt
, args
);
1535 int noyes(char *fmt
, ...)
1540 va_start(args
, fmt
);
1541 ret
= question(0, fmt
, args
);
1547 int pm_printf(alpm_loglevel_t level
, const char *format
, ...)
1552 /* print the message using va_arg list */
1553 va_start(args
, format
);
1554 ret
= pm_vfprintf(stderr
, level
, format
, args
);
1560 int pm_asprintf(char **string
, const char *format
, ...)
1565 /* print the message using va_arg list */
1566 va_start(args
, format
);
1567 if(vasprintf(string
, format
, args
) == -1) {
1568 pm_printf(ALPM_LOG_ERROR
, _("failed to allocate string\n"));
1576 int pm_vasprintf(char **string
, alpm_loglevel_t level
, const char *format
, va_list args
)
1581 /* if current logmask does not overlap with level, do not print msg */
1582 if(!(config
->logmask
& level
)) {
1586 /* print the message using va_arg list */
1587 ret
= vasprintf(&msg
, format
, args
);
1589 /* print a prefix to the message */
1591 case ALPM_LOG_ERROR
:
1592 pm_asprintf(string
, _("error: %s"), msg
);
1594 case ALPM_LOG_WARNING
:
1595 pm_asprintf(string
, _("warning: %s"), msg
);
1597 case ALPM_LOG_DEBUG
:
1598 pm_asprintf(string
, "debug: %s", msg
);
1600 case ALPM_LOG_FUNCTION
:
1601 pm_asprintf(string
, "function: %s", msg
);
1604 pm_asprintf(string
, "%s", msg
);
1612 int pm_vfprintf(FILE *stream
, alpm_loglevel_t level
, const char *format
, va_list args
)
1616 /* if current logmask does not overlap with level, do not print msg */
1617 if(!(config
->logmask
& level
)) {
1621 #if defined(PACMAN_DEBUG)
1622 /* If debug is on, we'll timestamp the output */
1623 if(config
->logmask
& ALPM_LOG_DEBUG
) {
1626 char timestr
[10] = {0};
1629 tmp
= localtime(&t
);
1630 strftime(timestr
, 9, "%H:%M:%S", tmp
);
1633 fprintf(stream
, "[%s] ", timestr
);
1637 /* print a prefix to the message */
1639 case ALPM_LOG_ERROR
:
1640 fprintf(stream
, _("error: "));
1642 case ALPM_LOG_WARNING
:
1643 fprintf(stream
, _("warning: "));
1645 case ALPM_LOG_DEBUG
:
1646 fprintf(stream
, "debug: ");
1648 case ALPM_LOG_FUNCTION
:
1649 fprintf(stream
, "function: ");
1655 /* print the message using va_arg list */
1656 ret
= vfprintf(stream
, format
, args
);
1660 #ifndef HAVE_STRNDUP
1661 /* A quick and dirty implementation derived from glibc */
1662 static size_t strnlen(const char *s
, size_t max
)
1664 register const char *p
;
1665 for(p
= s
; *p
&& max
--; ++p
);
1669 char *strndup(const char *s
, size_t n
)
1671 size_t len
= strnlen(s
, n
);
1672 char *new = (char *) malloc(len
+ 1);
1678 return (char *)memcpy(new, s
, len
);
1682 /* vim: set ts=2 sw=2 noet: */