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
);
59 ret
= alpm_trans_init(config
->handle
, flags
, NULL
, NULL
, NULL
);
61 ret
= alpm_trans_init(config
->handle
, flags
, cb_trans_evt
, cb_trans_conv
,
72 void trans_init_error(void)
74 enum _alpm_errno_t err
= alpm_errno(config
->handle
);
75 pm_fprintf(stderr
, ALPM_LOG_ERROR
, _("failed to init transaction (%s)\n"),
77 if(err
== ALPM_ERR_HANDLE_LOCK
) {
78 fprintf(stderr
, _(" if you're sure a package manager is not already\n"
79 " running, you can remove %s\n"),
80 alpm_option_get_lockfile(config
->handle
));
84 int trans_release(void)
86 if(alpm_trans_release(config
->handle
) == -1) {
87 pm_fprintf(stderr
, ALPM_LOG_ERROR
, _("failed to release transaction (%s)\n"),
88 alpm_strerror(alpm_errno(config
->handle
)));
101 return !config
->print
;
103 return (config
->op_s_clean
|| config
->op_s_sync
||
104 (!config
->group
&& !config
->op_s_info
&& !config
->op_q_list
&&
105 !config
->op_s_search
&& !config
->print
));
111 int check_syncdbs(size_t need_repos
, int check_valid
)
115 alpm_list_t
*sync_dbs
= alpm_option_get_syncdbs(config
->handle
);
117 if(need_repos
&& sync_dbs
== NULL
) {
118 pm_printf(ALPM_LOG_ERROR
, _("no usable package repositories configured.\n"));
123 /* ensure all known dbs are valid */
124 for(i
= sync_dbs
; i
; i
= alpm_list_next(i
)) {
125 alpm_db_t
*db
= i
->data
;
126 if(alpm_db_get_valid(db
)) {
127 pm_printf(ALPM_LOG_ERROR
, _("database '%s' is not valid (%s)\n"),
128 alpm_db_get_name(db
), alpm_strerror(alpm_errno(config
->handle
)));
136 /* discard unhandled input on the terminal's input buffer */
137 static int flush_term_input(void) {
139 if(isatty(fileno(stdin
))) {
140 return tcflush(fileno(stdin
), TCIFLUSH
);
148 /* gets the current screen column width */
152 const int default_tty
= 80;
153 const int default_notty
= 0;
155 if(!isatty(fileno(stdout
))) {
156 return default_notty
;
161 if(ioctl(1, TIOCGSIZE
, &win
) == 0) {
162 termwidth
= win
.ts_cols
;
164 #elif defined(TIOCGWINSZ)
166 if(ioctl(1, TIOCGWINSZ
, &win
) == 0) {
167 termwidth
= win
.ws_col
;
170 return termwidth
<= 0 ? default_tty
: termwidth
;
173 /* does the same thing as 'rm -rf' */
174 int rmrf(const char *path
)
183 if(errno
== ENOENT
) {
185 } else if(errno
== EPERM
) {
187 } else if(errno
== EISDIR
) {
189 } else if(errno
== ENOTDIR
) {
192 /* not a directory */
196 dirp
= opendir(path
);
200 for(dp
= readdir(dirp
); dp
!= NULL
; dp
= readdir(dirp
)) {
203 sprintf(name
, "%s/%s", path
, dp
->d_name
);
204 if(strcmp(dp
->d_name
, "..") != 0 && strcmp(dp
->d_name
, ".") != 0) {
205 errflag
+= rmrf(name
);
217 /** Parse the basename of a program from a path.
218 * @param path path to parse basename from
220 * @return everything following the final '/'
222 const char *mbasename(const char *path
)
224 const char *last
= strrchr(path
, '/');
231 /** Parse the dirname of a program from a path.
232 * The path returned should be freed.
233 * @param path path to parse dirname from
235 * @return everything preceding the final '/'
237 char *mdirname(const char *path
)
241 /* null or empty path */
242 if(path
== NULL
|| path
== '\0') {
247 last
= strrchr(ret
, '/');
250 /* we found a '/', so terminate our string */
259 /* output a string, but wrap words properly with a specified indentation
261 void indentprint(const char *str
, int indent
)
266 const int cols
= getcols();
272 /* if we're not a tty, or our tty is not wide enough that wrapping even makes
273 * sense, print without indenting */
274 if(cols
== 0 || indent
> cols
) {
279 len
= strlen(str
) + 1;
280 wcstr
= calloc(len
, sizeof(wchar_t));
281 len
= mbstowcs(wcstr
, str
, len
);
291 const wchar_t *q
, *next
;
293 if(p
== NULL
|| *p
== L
' ') continue;
294 next
= wcschr(p
, L
' ');
296 next
= p
+ wcslen(p
);
298 /* len captures # cols */
302 len
+= wcwidth(*q
++);
304 if(len
> (cols
- cidx
- 1)) {
305 /* wrap to a newline and reindent */
306 printf("\n%-*s", indent
, "");
314 printf("%lc", (wint_t)*p
);
321 /* Convert a string to uppercase
323 char *strtoupper(char *str
)
328 (*ptr
) = (char)toupper((unsigned char)*ptr
);
334 /* Trim whitespace and newlines from a string
336 char *strtrim(char *str
)
340 if(str
== NULL
|| *str
== '\0') {
341 /* string is empty, so we're done. */
345 while(isspace((unsigned char)*pch
)) {
349 size_t len
= strlen(pch
);
351 memmove(str
, pch
, len
+ 1);
357 /* check if there wasn't anything but whitespace in the string. */
362 pch
= (str
+ (strlen(str
) - 1));
363 while(isspace((unsigned char)*pch
)) {
371 /* Replace all occurances of 'needle' with 'replace' in 'str', returning
372 * a new string (must be free'd) */
373 char *strreplace(const char *str
, const char *needle
, const char *replace
)
375 const char *p
= NULL
, *q
= NULL
;
376 char *newstr
= NULL
, *newp
= NULL
;
377 alpm_list_t
*i
= NULL
, *list
= NULL
;
378 size_t needlesz
= strlen(needle
), replacesz
= strlen(replace
);
386 q
= strstr(p
, needle
);
388 list
= alpm_list_add(list
, (char *)q
);
390 q
= strstr(p
, needle
);
393 /* no occurences of needle found */
397 /* size of new string = size of old string + "number of occurences of needle"
398 * x "size difference between replace and needle" */
399 newsz
= strlen(str
) + 1 +
400 alpm_list_count(list
) * (replacesz
- needlesz
);
401 newstr
= calloc(newsz
, sizeof(char));
408 for(i
= list
; i
; i
= alpm_list_next(i
)) {
409 q
= alpm_list_getdata(i
);
411 /* add chars between this occurence and last occurence, if any */
412 memcpy(newp
, p
, (size_t)(q
- p
));
415 memcpy(newp
, replace
, replacesz
);
419 alpm_list_free(list
);
422 /* add the rest of 'p' */
429 /** Splits a string into a list of strings using the chosen character as
432 * @param str the string to split
433 * @param splitchar the character to split at
435 * @return a list containing the duplicated strings
437 alpm_list_t
*strsplit(const char *str
, const char splitchar
)
439 alpm_list_t
*list
= NULL
;
440 const char *prev
= str
;
443 while((str
= strchr(str
, splitchar
))) {
444 dup
= strndup(prev
, (size_t)(str
- prev
));
448 list
= alpm_list_add(list
, dup
);
458 list
= alpm_list_add(list
, dup
);
463 static int string_length(const char *s
)
471 /* len goes from # bytes -> # chars -> # cols */
473 wcstr
= calloc(len
, sizeof(wchar_t));
474 len
= mbstowcs(wcstr
, s
, len
);
475 len
= wcswidth(wcstr
, len
);
481 void string_display(const char *title
, const char *string
)
484 printf("%s ", title
);
486 if(string
== NULL
|| string
[0] == '\0') {
489 /* compute the length of title + a space */
490 int len
= string_length(title
) + 1;
491 indentprint(string
, len
);
496 static void table_print_line(const alpm_list_t
*line
,
497 const alpm_list_t
*formats
)
499 const alpm_list_t
*curformat
= formats
;
500 const alpm_list_t
*curcell
= line
;
502 while(curcell
&& curformat
) {
503 printf(alpm_list_getdata(curformat
), alpm_list_getdata(curcell
));
504 curcell
= alpm_list_next(curcell
);
505 curformat
= alpm_list_next(curformat
);
511 /* creates format strings by checking max cell lengths in cols */
512 static alpm_list_t
*table_create_format(const alpm_list_t
*header
,
513 const alpm_list_t
*rows
)
515 alpm_list_t
*longest_str
, *longest_strs
= NULL
;
516 alpm_list_t
*formats
= NULL
;
517 const alpm_list_t
*i
, *row
, *cell
;
518 char *str
, *formatstr
;
519 const int padding
= 2;
520 int colwidth
, totalwidth
= 0;
523 /* header determines column count and initial values of longest_strs */
524 for(i
= header
; i
; i
= alpm_list_next(i
)) {
525 longest_strs
= alpm_list_add(longest_strs
, alpm_list_getdata(i
));
528 /* now find the longest string in each column */
529 for(longest_str
= longest_strs
; longest_str
;
530 longest_str
= alpm_list_next(longest_str
), curcol
++) {
531 for(i
= rows
; i
; i
= alpm_list_next(i
)) {
532 row
= alpm_list_getdata(i
);
533 cell
= alpm_list_nth(row
, curcol
);
534 str
= alpm_list_getdata(cell
);
536 if(strlen(str
) > strlen(alpm_list_getdata(longest_str
))) {
537 longest_str
->data
= str
;
542 /* now use the column width info to generate format strings */
543 for(i
= longest_strs
; i
; i
= alpm_list_next(i
)) {
545 colwidth
= string_length(alpm_list_getdata(i
)) + padding
;
546 totalwidth
+= colwidth
;
548 /* right align the last column for a cleaner table display */
549 display
= (alpm_list_next(i
) != NULL
) ? "%%-%ds" : "%%%ds";
550 pm_asprintf(&formatstr
, display
, colwidth
);
552 formats
= alpm_list_add(formats
, formatstr
);
555 alpm_list_free(longest_strs
);
557 /* return NULL if terminal is not wide enough */
558 if(totalwidth
> getcols()) {
559 fprintf(stderr
, _("insufficient columns available for table display\n"));
567 /** Displays the list in table format
569 * @param title the tables title
570 * @param header the column headers. column count is determined by the nr
572 * @param rows the rows to display as a list of lists of strings. the outer
573 * list represents the rows, the inner list the cells (= columns)
575 * @return -1 if not enough terminal cols available, else 0
577 int table_display(const char *title
, const alpm_list_t
*header
,
578 const alpm_list_t
*rows
)
580 const alpm_list_t
*i
;
581 alpm_list_t
*formats
;
583 if(rows
== NULL
|| header
== NULL
) {
587 formats
= table_create_format(header
, rows
);
588 if(formats
== NULL
) {
593 printf("%s\n\n", title
);
596 table_print_line(header
, formats
);
599 for(i
= rows
; i
; i
= alpm_list_next(i
)) {
600 table_print_line(alpm_list_getdata(i
), formats
);
607 void list_display(const char *title
, const alpm_list_t
*list
)
609 const alpm_list_t
*i
;
613 len
= string_length(title
) + 1;
614 printf("%s ", title
);
618 printf("%s\n", _("None"));
620 const int maxcols
= getcols();
622 const char *str
= alpm_list_getdata(list
);
624 cols
+= string_length(str
);
625 for(i
= alpm_list_next(list
); i
; i
= alpm_list_next(i
)) {
626 str
= alpm_list_getdata(i
);
627 int s
= string_length(str
);
628 /* wrap only if we have enough usable column space */
629 if(maxcols
> len
&& cols
+ s
+ 2 >= maxcols
) {
633 for (j
= 1; j
<= len
; j
++) {
636 } else if(cols
!= len
) {
637 /* 2 spaces are added if this is not the first element on a line. */
648 void list_display_linebreak(const char *title
, const alpm_list_t
*list
)
653 len
= string_length(title
) + 1;
654 printf("%s ", title
);
658 printf("%s\n", _("None"));
660 const alpm_list_t
*i
;
661 /* Print the first element */
662 indentprint((const char *) alpm_list_getdata(list
), len
);
665 for(i
= alpm_list_next(list
); i
; i
= alpm_list_next(i
)) {
667 for(j
= 1; j
<= len
; j
++) {
670 indentprint((const char *) alpm_list_getdata(i
), len
);
676 void signature_display(const char *title
, alpm_siglist_t
*siglist
)
681 len
= string_length(title
) + 1;
682 printf("%s ", title
);
684 if(siglist
->count
== 0) {
688 for(i
= 0; i
< siglist
->count
; i
++) {
689 char sigline
[PATH_MAX
];
690 const char *status
, *validity
, *name
;
691 alpm_sigresult_t
*result
= siglist
->results
+ i
;
692 /* Don't re-indent the first result */
695 for(j
= 1; j
<= len
; j
++) {
699 switch(result
->status
) {
700 case ALPM_SIGSTATUS_VALID
:
703 case ALPM_SIGSTATUS_KEY_EXPIRED
:
704 status
= _("Key expired");
706 case ALPM_SIGSTATUS_SIG_EXPIRED
:
707 status
= _("Expired");
709 case ALPM_SIGSTATUS_INVALID
:
710 status
= _("Invalid");
712 case ALPM_SIGSTATUS_KEY_UNKNOWN
:
713 status
= _("Key unknown");
716 status
= _("Signature error");
719 switch(result
->validity
) {
720 case ALPM_SIGVALIDITY_FULL
:
721 validity
= _("full trust");
723 case ALPM_SIGVALIDITY_MARGINAL
:
724 validity
= _("marginal trust");
726 case ALPM_SIGVALIDITY_NEVER
:
727 validity
= _("never trust");
729 case ALPM_SIGVALIDITY_UNKNOWN
:
731 validity
= _("unknown trust");
734 name
= result
->key
.uid
? result
->key
.uid
: result
->key
.fingerprint
;
735 snprintf(sigline
, PATH_MAX
, _("%s, %s from \"%s\""),
736 status
, validity
, name
);
737 indentprint(sigline
, len
);
743 /* creates a header row for use with table_display */
744 static alpm_list_t
*create_verbose_header(int install
)
746 alpm_list_t
*res
= NULL
;
749 pm_asprintf(&str
, "%s", _("Name"));
750 res
= alpm_list_add(res
, str
);
751 pm_asprintf(&str
, "%s", _("Old Version"));
752 res
= alpm_list_add(res
, str
);
754 pm_asprintf(&str
, "%s", _("New Version"));
755 res
= alpm_list_add(res
, str
);
757 pm_asprintf(&str
, "%s", _("Size"));
758 res
= alpm_list_add(res
, str
);
763 /* returns package info as list of strings */
764 static alpm_list_t
*create_verbose_row(alpm_pkg_t
*pkg
, int install
)
769 alpm_list_t
*ret
= NULL
;
770 alpm_db_t
*ldb
= alpm_option_get_localdb(config
->handle
);
772 /* a row consists of the package name, */
773 pm_asprintf(&str
, "%s", alpm_pkg_get_name(pkg
));
774 ret
= alpm_list_add(ret
, str
);
776 /* old and new versions */
778 alpm_pkg_t
*oldpkg
= alpm_db_get_pkg(ldb
, alpm_pkg_get_name(pkg
));
779 pm_asprintf(&str
, "%s",
780 oldpkg
!= NULL
? alpm_pkg_get_version(oldpkg
) : "");
781 ret
= alpm_list_add(ret
, str
);
784 pm_asprintf(&str
, "%s", alpm_pkg_get_version(pkg
));
785 ret
= alpm_list_add(ret
, str
);
788 size
= humanize_size(alpm_pkg_get_size(pkg
), 'M', &label
);
789 pm_asprintf(&str
, "%.2f %s", size
, label
);
790 ret
= alpm_list_add(ret
, str
);
795 /* prepare a list of pkgs to display */
796 void display_targets(const alpm_list_t
*pkgs
, int install
)
799 const char *title
, *label
;
801 const alpm_list_t
*i
;
802 off_t isize
= 0, rsize
= 0, dlsize
= 0;
803 alpm_list_t
*j
, *lp
, *header
= NULL
, *targets
= NULL
;
804 alpm_db_t
*db_local
= alpm_option_get_localdb(config
->handle
);
810 /* gather pkg infos */
811 for(i
= pkgs
; i
; i
= alpm_list_next(i
)) {
812 alpm_pkg_t
*pkg
= alpm_list_getdata(i
);
815 alpm_pkg_t
*lpkg
= alpm_db_get_pkg(db_local
, alpm_pkg_get_name(pkg
));
816 dlsize
+= alpm_pkg_download_size(pkg
);
818 /* add up size of all removed packages */
819 rsize
+= alpm_pkg_get_isize(lpkg
);
822 isize
+= alpm_pkg_get_isize(pkg
);
824 if(config
->verbosepkglists
) {
825 targets
= alpm_list_add(targets
, create_verbose_row(pkg
, install
));
827 pm_asprintf(&str
, "%s-%s", alpm_pkg_get_name(pkg
),
828 alpm_pkg_get_version(pkg
));
829 targets
= alpm_list_add(targets
, str
);
833 /* print to screen */
834 title
= install
? _("Targets (%d):") : _("Remove (%d):");
835 pm_asprintf(&str
, title
, alpm_list_count(pkgs
));
838 if(config
->verbosepkglists
) {
839 header
= create_verbose_header(install
);
840 if(table_display(str
, header
, targets
) != 0) {
841 config
->verbosepkglists
= 0;
842 display_targets(pkgs
, install
);
846 list_display(str
, targets
);
851 size
= humanize_size(dlsize
, 'M', &label
);
852 printf(_("Total Download Size: %.2f %s\n"), size
, label
);
853 if(!(config
->flags
& ALPM_TRANS_FLAG_DOWNLOADONLY
)) {
854 size
= humanize_size(isize
, 'M', &label
);
855 printf(_("Total Installed Size: %.2f %s\n"), size
, label
);
856 /* only show this net value if different from raw installed size */
858 size
= humanize_size(isize
- rsize
, 'M', &label
);
859 printf(_("Net Upgrade Size: %.2f %s\n"), size
, label
);
863 size
= humanize_size(isize
, 'M', &label
);
864 printf(_("Total Removed Size: %.2f %s\n"), size
, label
);
869 if(config
->verbosepkglists
) {
870 /* targets is a list of lists of strings, free inner lists here */
871 for(j
= targets
; j
; j
= alpm_list_next(j
)) {
872 lp
= alpm_list_getdata(j
);
875 alpm_list_free(targets
);
883 static off_t
pkg_get_size(alpm_pkg_t
*pkg
)
887 return alpm_pkg_download_size(pkg
);
889 return alpm_pkg_get_size(pkg
);
891 return alpm_pkg_get_isize(pkg
);
895 static char *pkg_get_location(alpm_pkg_t
*pkg
)
897 alpm_list_t
*servers
;
901 servers
= alpm_db_get_servers(alpm_pkg_get_db(pkg
));
903 pm_asprintf(&string
, "%s/%s", alpm_list_getdata(servers
),
904 alpm_pkg_get_filename(pkg
));
908 return strdup(alpm_pkg_get_filename(pkg
));
910 pm_asprintf(&string
, "%s-%s", alpm_pkg_get_name(pkg
), alpm_pkg_get_version(pkg
));
915 /** Converts sizes in bytes into human readable units.
917 * @param bytes the size in bytes
918 * @param target_unit '\0' or a short label. If equal to one of the short unit
919 * labels ('B', 'K', ...) bytes is converted to target_unit; if '\0', the first
920 * unit which will bring the value to below a threshold of 2048 will be chosen.
921 * @param long_labels whether to use short ("K") or long ("KiB") unit labels
922 * @param label will be set to the appropriate unit label
924 * @return the size in the appropriate unit
926 double humanize_size(off_t bytes
, const char target_unit
, const char **label
)
928 static const char *labels
[] = {"B", "KiB", "MiB", "GiB",
929 "TiB", "PiB", "EiB", "ZiB", "YiB"};
930 static const int unitcount
= sizeof(labels
) / sizeof(labels
[0]);
932 double val
= (double)bytes
;
935 for(index
= 0; index
< unitcount
- 1; index
++) {
936 if(target_unit
!= '\0' && labels
[index
][0] == target_unit
) {
938 } else if(target_unit
== '\0' && val
<= 2048.0 && val
>= -2048.0) {
945 *label
= labels
[index
];
951 void print_packages(const alpm_list_t
*packages
)
953 const alpm_list_t
*i
;
954 if(!config
->print_format
) {
955 config
->print_format
= strdup("%l");
957 for(i
= packages
; i
; i
= alpm_list_next(i
)) {
958 alpm_pkg_t
*pkg
= alpm_list_getdata(i
);
959 char *string
= strdup(config
->print_format
);
962 if(strstr(temp
,"%n")) {
963 string
= strreplace(temp
, "%n", alpm_pkg_get_name(pkg
));
968 if(strstr(temp
,"%v")) {
969 string
= strreplace(temp
, "%v", alpm_pkg_get_version(pkg
));
974 if(strstr(temp
,"%l")) {
975 char *pkgloc
= pkg_get_location(pkg
);
976 string
= strreplace(temp
, "%l", pkgloc
);
982 if(strstr(temp
,"%r")) {
983 const char *repo
= "local";
984 alpm_db_t
*db
= alpm_pkg_get_db(pkg
);
986 repo
= alpm_db_get_name(db
);
988 string
= strreplace(temp
, "%r", repo
);
993 if(strstr(temp
,"%s")) {
995 pm_asprintf(&size
, "%jd", (intmax_t)pkg_get_size(pkg
));
996 string
= strreplace(temp
, "%s", size
);
1000 printf("%s\n",string
);
1005 /* Helper function for comparing strings using the
1006 * alpm "compare func" signature */
1007 int str_cmp(const void *s1
, const void *s2
)
1009 return strcmp(s1
, s2
);
1012 void display_new_optdepends(alpm_pkg_t
*oldpkg
, alpm_pkg_t
*newpkg
)
1014 alpm_list_t
*old
= alpm_pkg_get_optdepends(oldpkg
);
1015 alpm_list_t
*new = alpm_pkg_get_optdepends(newpkg
);
1016 alpm_list_t
*optdeps
= alpm_list_diff(new,old
,str_cmp
);
1018 printf(_("New optional dependencies for %s\n"), alpm_pkg_get_name(newpkg
));
1019 list_display_linebreak(" ", optdeps
);
1021 alpm_list_free(optdeps
);
1024 void display_optdepends(alpm_pkg_t
*pkg
)
1026 alpm_list_t
*optdeps
= alpm_pkg_get_optdepends(pkg
);
1028 printf(_("Optional dependencies for %s\n"), alpm_pkg_get_name(pkg
));
1029 list_display_linebreak(" ", optdeps
);
1033 static void display_repo_list(const char *dbname
, alpm_list_t
*list
)
1035 const char *prefix
= " ";
1038 printf(_("Repository %s\n"), dbname
);
1039 list_display(prefix
, list
);
1042 void select_display(const alpm_list_t
*pkglist
)
1044 const alpm_list_t
*i
;
1046 alpm_list_t
*list
= NULL
;
1047 char *string
= NULL
;
1048 const char *dbname
= NULL
;
1050 for (i
= pkglist
; i
; i
= i
->next
) {
1051 alpm_pkg_t
*pkg
= alpm_list_getdata(i
);
1052 alpm_db_t
*db
= alpm_pkg_get_db(pkg
);
1055 dbname
= alpm_db_get_name(db
);
1056 if(strcmp(alpm_db_get_name(db
), dbname
) != 0) {
1057 display_repo_list(dbname
, list
);
1059 dbname
= alpm_db_get_name(db
);
1062 pm_asprintf(&string
, "%d) %s", nth
, alpm_pkg_get_name(pkg
));
1063 list
= alpm_list_add(list
, string
);
1066 display_repo_list(dbname
, list
);
1070 static int parseindex(char *s
, int *val
, int min
, int max
)
1072 char *endptr
= NULL
;
1073 int n
= strtol(s
, &endptr
, 10);
1074 if(*endptr
== '\0') {
1075 if(n
< min
|| n
> max
) {
1076 fprintf(stderr
, _("Invalid value: %d is not between %d and %d\n"),
1083 fprintf(stderr
, _("Invalid number: %s\n"), s
);
1088 static int multiselect_parse(char *array
, int count
, char *response
)
1090 char *str
, *saveptr
;
1092 for (str
= response
; ; str
= NULL
) {
1096 char *starts
= strtok_r(str
, " ", &saveptr
);
1098 if(starts
== NULL
) {
1102 int len
= strlen(starts
);
1106 if(*starts
== '^') {
1111 /* if first token is including, we unselect all targets */
1112 memset(array
, 0, count
);
1116 /* check for range */
1118 if((p
= strchr(starts
+ 1, '-'))) {
1124 if(parseindex(starts
, &start
, 1, count
) != 0)
1128 array
[start
-1] = include
;
1131 if(parseindex(ends
, &end
, start
, count
) != 0) {
1134 for(d
= start
; d
<= end
; d
++) {
1135 array
[d
-1] = include
;
1143 int multiselect_question(char *array
, int count
)
1145 char *response
, *lastchar
;
1147 size_t response_len
= 64;
1149 if(config
->noconfirm
) {
1152 /* Use stderr so questions are always displayed when redirecting output */
1156 response
= malloc(response_len
);
1160 lastchar
= response
+ response_len
- 1;
1161 /* sentinel byte to later see if we filled up the entire string */
1165 memset(array
, 1, count
);
1167 fprintf(stream
, "\n");
1168 fprintf(stream
, _("Enter a selection (default=all)"));
1169 fprintf(stream
, ": ");
1172 if(config
->noconfirm
) {
1173 fprintf(stream
, "\n");
1179 if(fgets(response
, response_len
, stdin
)) {
1180 const size_t response_incr
= 64;
1181 /* handle buffer not being large enough to read full line case */
1182 while(*lastchar
== '\0' && lastchar
[-1] != '\n') {
1183 response_len
+= response_incr
;
1184 response
= realloc(response
, response_len
);
1188 lastchar
= response
+ response_len
- 1;
1191 if(fgets(response
+ response_len
- response_incr
- 1,
1192 response_incr
+ 1, stdin
) == 0) {
1198 if(strlen(response
) > 0) {
1199 if(multiselect_parse(array
, count
, response
) == -1) {
1200 /* only loop if user gave an invalid answer */
1215 int select_question(int count
)
1221 if(config
->noconfirm
) {
1224 /* Use stderr so questions are always displayed when redirecting output */
1229 fprintf(stream
, "\n");
1230 fprintf(stream
, _("Enter a number (default=%d)"), preset
);
1231 fprintf(stream
, ": ");
1233 if(config
->noconfirm
) {
1234 fprintf(stream
, "\n");
1240 if(fgets(response
, sizeof(response
), stdin
)) {
1242 if(strlen(response
) > 0) {
1244 if(parseindex(response
, &n
, 1, count
) != 0)
1252 return (preset
- 1);
1256 /* presents a prompt and gets a Y/N answer */
1257 static int question(short preset
, char *fmt
, va_list args
)
1262 if(config
->noconfirm
) {
1265 /* Use stderr so questions are always displayed when redirecting output */
1269 /* ensure all text makes it to the screen before we prompt the user */
1273 vfprintf(stream
, fmt
, args
);
1276 fprintf(stream
, " %s ", _("[Y/n]"));
1278 fprintf(stream
, " %s ", _("[y/N]"));
1281 if(config
->noconfirm
) {
1282 fprintf(stream
, "\n");
1289 if(fgets(response
, sizeof(response
), stdin
)) {
1291 if(strlen(response
) == 0) {
1295 if(strcasecmp(response
, _("Y")) == 0 || strcasecmp(response
, _("YES")) == 0) {
1297 } else if(strcasecmp(response
, _("N")) == 0 || strcasecmp(response
, _("NO")) == 0) {
1304 int yesno(char *fmt
, ...)
1309 va_start(args
, fmt
);
1310 ret
= question(1, fmt
, args
);
1316 int noyes(char *fmt
, ...)
1321 va_start(args
, fmt
);
1322 ret
= question(0, fmt
, args
);
1328 int pm_printf(alpm_loglevel_t level
, const char *format
, ...)
1333 /* print the message using va_arg list */
1334 va_start(args
, format
);
1335 ret
= pm_vfprintf(stdout
, level
, format
, args
);
1341 int pm_fprintf(FILE *stream
, alpm_loglevel_t level
, const char *format
, ...)
1346 /* print the message using va_arg list */
1347 va_start(args
, format
);
1348 ret
= pm_vfprintf(stream
, level
, format
, args
);
1354 int pm_asprintf(char **string
, const char *format
, ...)
1359 /* print the message using va_arg list */
1360 va_start(args
, format
);
1361 if(vasprintf(string
, format
, args
) == -1) {
1362 pm_fprintf(stderr
, ALPM_LOG_ERROR
, _("failed to allocate string\n"));
1370 int pm_vasprintf(char **string
, alpm_loglevel_t level
, const char *format
, va_list args
)
1375 /* if current logmask does not overlap with level, do not print msg */
1376 if(!(config
->logmask
& level
)) {
1380 /* print the message using va_arg list */
1381 ret
= vasprintf(&msg
, format
, args
);
1383 /* print a prefix to the message */
1385 case ALPM_LOG_ERROR
:
1386 pm_asprintf(string
, _("error: %s"), msg
);
1388 case ALPM_LOG_WARNING
:
1389 pm_asprintf(string
, _("warning: %s"), msg
);
1391 case ALPM_LOG_DEBUG
:
1392 pm_asprintf(string
, "debug: %s", msg
);
1394 case ALPM_LOG_FUNCTION
:
1395 pm_asprintf(string
, "function: %s", msg
);
1398 pm_asprintf(string
, "%s", msg
);
1406 int pm_vfprintf(FILE *stream
, alpm_loglevel_t level
, const char *format
, va_list args
)
1410 /* if current logmask does not overlap with level, do not print msg */
1411 if(!(config
->logmask
& level
)) {
1415 #if defined(PACMAN_DEBUG)
1416 /* If debug is on, we'll timestamp the output */
1417 if(config
->logmask
& ALPM_LOG_DEBUG
) {
1420 char timestr
[10] = {0};
1423 tmp
= localtime(&t
);
1424 strftime(timestr
, 9, "%H:%M:%S", tmp
);
1427 fprintf(stream
, "[%s] ", timestr
);
1431 /* print a prefix to the message */
1433 case ALPM_LOG_ERROR
:
1434 fprintf(stream
, _("error: "));
1436 case ALPM_LOG_WARNING
:
1437 fprintf(stream
, _("warning: "));
1439 case ALPM_LOG_DEBUG
:
1440 fprintf(stream
, "debug: ");
1442 case ALPM_LOG_FUNCTION
:
1443 fprintf(stream
, "function: ");
1449 /* print the message using va_arg list */
1450 ret
= vfprintf(stream
, format
, args
);
1454 #ifndef HAVE_STRNDUP
1455 /* A quick and dirty implementation derived from glibc */
1456 static size_t strnlen(const char *s
, size_t max
)
1458 register const char *p
;
1459 for(p
= s
; *p
&& max
--; ++p
);
1463 char *strndup(const char *s
, size_t n
)
1465 size_t len
= strnlen(s
, n
);
1466 char *new = (char *) malloc(len
+ 1);
1472 return (char *)memcpy(new, s
, len
);
1476 /* vim: set ts=2 sw=2 noet: */