Improve advice for sync ops when db.lck is present
[pacman-ng.git] / src / pacman / util.c
blob599c18c4df69455844c0893969ad069336e1af4e
1 /*
2 * util.c
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/>.
21 #include "config.h"
23 #include <sys/types.h>
24 #include <sys/ioctl.h>
25 #include <sys/stat.h>
26 #include <sys/time.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <stdarg.h>
31 #include <stdint.h> /* intmax_t */
32 #include <string.h>
33 #include <errno.h>
34 #include <ctype.h>
35 #include <dirent.h>
36 #include <unistd.h>
37 #include <limits.h>
38 #include <wchar.h>
39 #ifdef HAVE_TERMIOS_H
40 #include <termios.h> /* tcflush */
41 #endif
43 #include <alpm.h>
44 #include <alpm_list.h>
46 /* pacman */
47 #include "util.h"
48 #include "conf.h"
49 #include "callback.h"
52 int trans_init(alpm_transflag_t flags, int check_valid)
54 int ret;
56 check_syncdbs(0, check_valid);
58 if(config->print) {
59 ret = alpm_trans_init(config->handle, flags, NULL, NULL, NULL);
60 } else {
61 ret = alpm_trans_init(config->handle, flags, cb_trans_evt, cb_trans_conv,
62 cb_trans_progress);
65 if(ret == -1) {
66 trans_init_error();
67 return -1;
69 return 0;
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"),
76 alpm_strerror(err));
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)));
89 return -1;
91 return 0;
94 int needs_root(void)
96 switch(config->op) {
97 case PM_OP_DATABASE:
98 return 1;
99 case PM_OP_UPGRADE:
100 case PM_OP_REMOVE:
101 return !config->print;
102 case PM_OP_SYNC:
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));
106 default:
107 return 0;
111 int check_syncdbs(size_t need_repos, int check_valid)
113 int ret = 0;
114 alpm_list_t *i;
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"));
119 return 1;
122 if(check_valid) {
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)));
129 ret = 1;
133 return ret;
136 /* discard unhandled input on the terminal's input buffer */
137 static int flush_term_input(void) {
138 #ifdef HAVE_TCFLUSH
139 if(isatty(fileno(stdin))) {
140 return tcflush(fileno(stdin), TCIFLUSH);
142 #endif
144 /* fail silently */
145 return 0;
148 /* gets the current screen column width */
149 int getcols()
151 int termwidth = -1;
152 const int default_tty = 80;
153 const int default_notty = 0;
155 if(!isatty(fileno(stdout))) {
156 return default_notty;
159 #ifdef TIOCGSIZE
160 struct ttysize win;
161 if(ioctl(1, TIOCGSIZE, &win) == 0) {
162 termwidth = win.ts_cols;
164 #elif defined(TIOCGWINSZ)
165 struct winsize win;
166 if(ioctl(1, TIOCGWINSZ, &win) == 0) {
167 termwidth = win.ws_col;
169 #endif
170 return termwidth <= 0 ? default_tty : termwidth;
173 /* does the same thing as 'rm -rf' */
174 int rmrf(const char *path)
176 int errflag = 0;
177 struct dirent *dp;
178 DIR *dirp;
180 if(!unlink(path)) {
181 return 0;
182 } else {
183 if(errno == ENOENT) {
184 return 0;
185 } else if(errno == EPERM) {
186 /* fallthrough */
187 } else if(errno == EISDIR) {
188 /* fallthrough */
189 } else if(errno == ENOTDIR) {
190 return 1;
191 } else {
192 /* not a directory */
193 return 1;
196 dirp = opendir(path);
197 if(!dirp) {
198 return 1;
200 for(dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
201 if(dp->d_ino) {
202 char name[PATH_MAX];
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);
209 closedir(dirp);
210 if(rmdir(path)) {
211 errflag++;
213 return errflag;
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, '/');
225 if(last) {
226 return last + 1;
228 return 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)
239 char *ret, *last;
241 /* null or empty path */
242 if(path == NULL || path == '\0') {
243 return strdup(".");
246 ret = strdup(path);
247 last = strrchr(ret, '/');
249 if(last != NULL) {
250 /* we found a '/', so terminate our string */
251 *last = '\0';
252 return ret;
254 /* no slash found */
255 free(ret);
256 return strdup(".");
259 /* output a string, but wrap words properly with a specified indentation
261 void indentprint(const char *str, int indent)
263 wchar_t *wcstr;
264 const wchar_t *p;
265 int len, cidx;
266 const int cols = getcols();
268 if(!str) {
269 return;
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) {
275 printf("%s", str);
276 return;
279 len = strlen(str) + 1;
280 wcstr = calloc(len, sizeof(wchar_t));
281 len = mbstowcs(wcstr, str, len);
282 p = wcstr;
283 cidx = indent;
285 if(!p || !len) {
286 return;
289 while(*p) {
290 if(*p == L' ') {
291 const wchar_t *q, *next;
292 p++;
293 if(p == NULL || *p == L' ') continue;
294 next = wcschr(p, L' ');
295 if(next == NULL) {
296 next = p + wcslen(p);
298 /* len captures # cols */
299 len = 0;
300 q = p;
301 while(q < next) {
302 len += wcwidth(*q++);
304 if(len > (cols - cidx - 1)) {
305 /* wrap to a newline and reindent */
306 printf("\n%-*s", indent, "");
307 cidx = indent;
308 } else {
309 printf(" ");
310 cidx++;
312 continue;
314 printf("%lc", (wint_t)*p);
315 cidx += wcwidth(*p);
316 p++;
318 free(wcstr);
321 /* Convert a string to uppercase
323 char *strtoupper(char *str)
325 char *ptr = str;
327 while(*ptr) {
328 (*ptr) = (char)toupper((unsigned char)*ptr);
329 ptr++;
331 return str;
334 /* Trim whitespace and newlines from a string
336 char *strtrim(char *str)
338 char *pch = str;
340 if(str == NULL || *str == '\0') {
341 /* string is empty, so we're done. */
342 return str;
345 while(isspace((unsigned char)*pch)) {
346 pch++;
348 if(pch != str) {
349 size_t len = strlen(pch);
350 if(len) {
351 memmove(str, pch, len + 1);
352 } else {
353 *str = '\0';
357 /* check if there wasn't anything but whitespace in the string. */
358 if(*str == '\0') {
359 return str;
362 pch = (str + (strlen(str) - 1));
363 while(isspace((unsigned char)*pch)) {
364 pch--;
366 *++pch = '\0';
368 return str;
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);
379 size_t newsz;
381 if(!str) {
382 return NULL;
385 p = str;
386 q = strstr(p, needle);
387 while(q) {
388 list = alpm_list_add(list, (char *)q);
389 p = q + needlesz;
390 q = strstr(p, needle);
393 /* no occurences of needle found */
394 if(!list) {
395 return strdup(str);
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));
402 if(!newstr) {
403 return NULL;
406 p = str;
407 newp = newstr;
408 for(i = list; i; i = alpm_list_next(i)) {
409 q = alpm_list_getdata(i);
410 if(q > p) {
411 /* add chars between this occurence and last occurence, if any */
412 memcpy(newp, p, (size_t)(q - p));
413 newp += q - p;
415 memcpy(newp, replace, replacesz);
416 newp += replacesz;
417 p = q + needlesz;
419 alpm_list_free(list);
421 if(*p) {
422 /* add the rest of 'p' */
423 strcpy(newp, p);
426 return newstr;
429 /** Splits a string into a list of strings using the chosen character as
430 * a delimiter.
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;
441 char *dup = NULL;
443 while((str = strchr(str, splitchar))) {
444 dup = strndup(prev, (size_t)(str - prev));
445 if(dup == NULL) {
446 return NULL;
448 list = alpm_list_add(list, dup);
450 str++;
451 prev = str;
454 dup = strdup(prev);
455 if(dup == NULL) {
456 return NULL;
458 list = alpm_list_add(list, dup);
460 return list;
463 static int string_length(const char *s)
465 int len;
466 wchar_t *wcstr;
468 if(!s) {
469 return 0;
471 /* len goes from # bytes -> # chars -> # cols */
472 len = strlen(s) + 1;
473 wcstr = calloc(len, sizeof(wchar_t));
474 len = mbstowcs(wcstr, s, len);
475 len = wcswidth(wcstr, len);
476 free(wcstr);
478 return len;
481 void string_display(const char *title, const char *string)
483 if(title) {
484 printf("%s ", title);
486 if(string == NULL || string[0] == '\0') {
487 printf(_("None"));
488 } else {
489 /* compute the length of title + a space */
490 int len = string_length(title) + 1;
491 indentprint(string, len);
493 printf("\n");
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);
508 printf("\n");
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;
521 int curcol = 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)) {
544 const char *display;
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"));
560 FREELIST(formats);
561 return NULL;
564 return formats;
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
571 * of headers
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) {
584 return 0;
587 formats = table_create_format(header, rows);
588 if(formats == NULL) {
589 return -1;
592 if(title != NULL) {
593 printf("%s\n\n", title);
596 table_print_line(header, formats);
597 printf("\n");
599 for(i = rows; i; i = alpm_list_next(i)) {
600 table_print_line(alpm_list_getdata(i), formats);
603 FREELIST(formats);
604 return 0;
607 void list_display(const char *title, const alpm_list_t *list)
609 const alpm_list_t *i;
610 int len = 0;
612 if(title) {
613 len = string_length(title) + 1;
614 printf("%s ", title);
617 if(!list) {
618 printf("%s\n", _("None"));
619 } else {
620 const int maxcols = getcols();
621 int cols = len;
622 const char *str = alpm_list_getdata(list);
623 printf("%s", str);
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) {
630 int j;
631 cols = len;
632 printf("\n");
633 for (j = 1; j <= len; j++) {
634 printf(" ");
636 } else if(cols != len) {
637 /* 2 spaces are added if this is not the first element on a line. */
638 printf(" ");
639 cols += 2;
641 printf("%s", str);
642 cols += s;
644 printf("\n");
648 void list_display_linebreak(const char *title, const alpm_list_t *list)
650 int len = 0;
652 if(title) {
653 len = string_length(title) + 1;
654 printf("%s ", title);
657 if(!list) {
658 printf("%s\n", _("None"));
659 } else {
660 const alpm_list_t *i;
661 /* Print the first element */
662 indentprint((const char *) alpm_list_getdata(list), len);
663 printf("\n");
664 /* Print the rest */
665 for(i = alpm_list_next(list); i; i = alpm_list_next(i)) {
666 int j;
667 for(j = 1; j <= len; j++) {
668 printf(" ");
670 indentprint((const char *) alpm_list_getdata(i), len);
671 printf("\n");
676 void signature_display(const char *title, alpm_siglist_t *siglist)
678 int len = 0;
680 if(title) {
681 len = string_length(title) + 1;
682 printf("%s ", title);
684 if(siglist->count == 0) {
685 printf(_("None"));
686 } else {
687 size_t i;
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 */
693 if(i != 0) {
694 int j;
695 for(j = 1; j <= len; j++) {
696 printf(" ");
699 switch(result->status) {
700 case ALPM_SIGSTATUS_VALID:
701 status = _("Valid");
702 break;
703 case ALPM_SIGSTATUS_KEY_EXPIRED:
704 status = _("Key expired");
705 break;
706 case ALPM_SIGSTATUS_SIG_EXPIRED:
707 status = _("Expired");
708 break;
709 case ALPM_SIGSTATUS_INVALID:
710 status = _("Invalid");
711 break;
712 case ALPM_SIGSTATUS_KEY_UNKNOWN:
713 status = _("Key unknown");
714 break;
715 default:
716 status = _("Signature error");
717 break;
719 switch(result->validity) {
720 case ALPM_SIGVALIDITY_FULL:
721 validity = _("full trust");
722 break;
723 case ALPM_SIGVALIDITY_MARGINAL:
724 validity = _("marginal trust");
725 break;
726 case ALPM_SIGVALIDITY_NEVER:
727 validity = _("never trust");
728 break;
729 case ALPM_SIGVALIDITY_UNKNOWN:
730 default:
731 validity = _("unknown trust");
732 break;
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);
738 printf("\n");
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;
747 char *str;
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);
753 if(install) {
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);
760 return res;
763 /* returns package info as list of strings */
764 static alpm_list_t *create_verbose_row(alpm_pkg_t *pkg, int install)
766 char *str;
767 double size;
768 const char *label;
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 */
777 if(install) {
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);
787 /* and size */
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);
792 return ret;
795 /* prepare a list of pkgs to display */
796 void display_targets(const alpm_list_t *pkgs, int install)
798 char *str;
799 const char *title, *label;
800 double size;
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);
806 if(!pkgs) {
807 return;
810 /* gather pkg infos */
811 for(i = pkgs; i; i = alpm_list_next(i)) {
812 alpm_pkg_t *pkg = alpm_list_getdata(i);
814 if(install) {
815 alpm_pkg_t *lpkg = alpm_db_get_pkg(db_local, alpm_pkg_get_name(pkg));
816 dlsize += alpm_pkg_download_size(pkg);
817 if(lpkg) {
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));
826 } else {
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));
837 printf("\n");
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);
843 goto out;
845 } else {
846 list_display(str, targets);
848 printf("\n");
850 if(install) {
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 */
857 if(rsize > 0) {
858 size = humanize_size(isize - rsize, 'M', &label);
859 printf(_("Net Upgrade Size: %.2f %s\n"), size, label);
862 } else {
863 size = humanize_size(isize, 'M', &label);
864 printf(_("Total Removed Size: %.2f %s\n"), size, label);
867 out:
868 /* cleanup */
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);
873 FREELIST(lp);
875 alpm_list_free(targets);
876 FREELIST(header);
877 } else {
878 FREELIST(targets);
880 free(str);
883 static off_t pkg_get_size(alpm_pkg_t *pkg)
885 switch(config->op) {
886 case PM_OP_SYNC:
887 return alpm_pkg_download_size(pkg);
888 case PM_OP_UPGRADE:
889 return alpm_pkg_get_size(pkg);
890 default:
891 return alpm_pkg_get_isize(pkg);
895 static char *pkg_get_location(alpm_pkg_t *pkg)
897 alpm_list_t *servers;
898 char *string = NULL;
899 switch(config->op) {
900 case PM_OP_SYNC:
901 servers = alpm_db_get_servers(alpm_pkg_get_db(pkg));
902 if(servers) {
903 pm_asprintf(&string, "%s/%s", alpm_list_getdata(servers),
904 alpm_pkg_get_filename(pkg));
905 return string;
907 case PM_OP_UPGRADE:
908 return strdup(alpm_pkg_get_filename(pkg));
909 default:
910 pm_asprintf(&string, "%s-%s", alpm_pkg_get_name(pkg), alpm_pkg_get_version(pkg));
911 return string;
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;
933 int index;
935 for(index = 0; index < unitcount - 1; index++) {
936 if(target_unit != '\0' && labels[index][0] == target_unit) {
937 break;
938 } else if(target_unit == '\0' && val <= 2048.0 && val >= -2048.0) {
939 break;
941 val /= 1024.0;
944 if(label) {
945 *label = labels[index];
948 return val;
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);
960 char *temp = string;
961 /* %n : pkgname */
962 if(strstr(temp,"%n")) {
963 string = strreplace(temp, "%n", alpm_pkg_get_name(pkg));
964 free(temp);
965 temp = string;
967 /* %v : pkgver */
968 if(strstr(temp,"%v")) {
969 string = strreplace(temp, "%v", alpm_pkg_get_version(pkg));
970 free(temp);
971 temp = string;
973 /* %l : location */
974 if(strstr(temp,"%l")) {
975 char *pkgloc = pkg_get_location(pkg);
976 string = strreplace(temp, "%l", pkgloc);
977 free(pkgloc);
978 free(temp);
979 temp = string;
981 /* %r : repo */
982 if(strstr(temp,"%r")) {
983 const char *repo = "local";
984 alpm_db_t *db = alpm_pkg_get_db(pkg);
985 if(db) {
986 repo = alpm_db_get_name(db);
988 string = strreplace(temp, "%r", repo);
989 free(temp);
990 temp = string;
992 /* %s : size */
993 if(strstr(temp,"%s")) {
994 char *size;
995 pm_asprintf(&size, "%jd", (intmax_t)pkg_get_size(pkg));
996 string = strreplace(temp, "%s", size);
997 free(size);
998 free(temp);
1000 printf("%s\n",string);
1001 free(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);
1017 if(optdeps) {
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);
1027 if(optdeps) {
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= " ";
1037 printf(":: ");
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;
1045 int nth = 1;
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);
1054 if(!dbname)
1055 dbname = alpm_db_get_name(db);
1056 if(strcmp(alpm_db_get_name(db), dbname) != 0) {
1057 display_repo_list(dbname, list);
1058 FREELIST(list);
1059 dbname = alpm_db_get_name(db);
1061 string = NULL;
1062 pm_asprintf(&string, "%d) %s", nth, alpm_pkg_get_name(pkg));
1063 list = alpm_list_add(list, string);
1064 nth++;
1066 display_repo_list(dbname, list);
1067 FREELIST(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"),
1077 n, min, max);
1078 return -1;
1080 *val = n;
1081 return 0;
1082 } else {
1083 fprintf(stderr, _("Invalid number: %s\n"), s);
1084 return -1;
1088 static int multiselect_parse(char *array, int count, char *response)
1090 char *str, *saveptr;
1092 for (str = response; ; str = NULL) {
1093 int include = 1;
1094 int start, end;
1095 char *ends = NULL;
1096 char *starts = strtok_r(str, " ", &saveptr);
1098 if(starts == NULL) {
1099 break;
1101 strtrim(starts);
1102 int len = strlen(starts);
1103 if(len == 0)
1104 continue;
1106 if(*starts == '^') {
1107 starts++;
1108 len--;
1109 include = 0;
1110 } else if(str) {
1111 /* if first token is including, we unselect all targets */
1112 memset(array, 0, count);
1115 if(len > 1) {
1116 /* check for range */
1117 char *p;
1118 if((p = strchr(starts + 1, '-'))) {
1119 *p = 0;
1120 ends = p + 1;
1124 if(parseindex(starts, &start, 1, count) != 0)
1125 return -1;
1127 if(!ends) {
1128 array[start-1] = include;
1129 } else {
1130 int d;
1131 if(parseindex(ends, &end, start, count) != 0) {
1132 return -1;
1134 for(d = start; d <= end; d++) {
1135 array[d-1] = include;
1140 return 0;
1143 int multiselect_question(char *array, int count)
1145 char *response, *lastchar;
1146 FILE *stream;
1147 size_t response_len = 64;
1149 if(config->noconfirm) {
1150 stream = stdout;
1151 } else {
1152 /* Use stderr so questions are always displayed when redirecting output */
1153 stream = stderr;
1156 response = malloc(response_len);
1157 if(!response) {
1158 return -1;
1160 lastchar = response + response_len - 1;
1161 /* sentinel byte to later see if we filled up the entire string */
1162 *lastchar = 1;
1164 while(1) {
1165 memset(array, 1, count);
1167 fprintf(stream, "\n");
1168 fprintf(stream, _("Enter a selection (default=all)"));
1169 fprintf(stream, ": ");
1170 fflush(stream);
1172 if(config->noconfirm) {
1173 fprintf(stream, "\n");
1174 break;
1177 flush_term_input();
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);
1185 if(!response) {
1186 return -1;
1188 lastchar = response + response_len - 1;
1189 /* sentinel byte */
1190 *lastchar = 1;
1191 if(fgets(response + response_len - response_incr - 1,
1192 response_incr + 1, stdin) == 0) {
1193 free(response);
1194 return -1;
1197 strtrim(response);
1198 if(strlen(response) > 0) {
1199 if(multiselect_parse(array, count, response) == -1) {
1200 /* only loop if user gave an invalid answer */
1201 continue;
1204 break;
1205 } else {
1206 free(response);
1207 return -1;
1211 free(response);
1212 return 0;
1215 int select_question(int count)
1217 char response[32];
1218 FILE *stream;
1219 int preset = 1;
1221 if(config->noconfirm) {
1222 stream = stdout;
1223 } else {
1224 /* Use stderr so questions are always displayed when redirecting output */
1225 stream = stderr;
1228 while(1) {
1229 fprintf(stream, "\n");
1230 fprintf(stream, _("Enter a number (default=%d)"), preset);
1231 fprintf(stream, ": ");
1233 if(config->noconfirm) {
1234 fprintf(stream, "\n");
1235 break;
1238 flush_term_input();
1240 if(fgets(response, sizeof(response), stdin)) {
1241 strtrim(response);
1242 if(strlen(response) > 0) {
1243 int n;
1244 if(parseindex(response, &n, 1, count) != 0)
1245 continue;
1246 return (n - 1);
1249 break;
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)
1259 char response[32];
1260 FILE *stream;
1262 if(config->noconfirm) {
1263 stream = stdout;
1264 } else {
1265 /* Use stderr so questions are always displayed when redirecting output */
1266 stream = stderr;
1269 /* ensure all text makes it to the screen before we prompt the user */
1270 fflush(stdout);
1271 fflush(stderr);
1273 vfprintf(stream, fmt, args);
1275 if(preset) {
1276 fprintf(stream, " %s ", _("[Y/n]"));
1277 } else {
1278 fprintf(stream, " %s ", _("[y/N]"));
1281 if(config->noconfirm) {
1282 fprintf(stream, "\n");
1283 return preset;
1286 fflush(stream);
1287 flush_term_input();
1289 if(fgets(response, sizeof(response), stdin)) {
1290 strtrim(response);
1291 if(strlen(response) == 0) {
1292 return preset;
1295 if(strcasecmp(response, _("Y")) == 0 || strcasecmp(response, _("YES")) == 0) {
1296 return 1;
1297 } else if(strcasecmp(response, _("N")) == 0 || strcasecmp(response, _("NO")) == 0) {
1298 return 0;
1301 return 0;
1304 int yesno(char *fmt, ...)
1306 int ret;
1307 va_list args;
1309 va_start(args, fmt);
1310 ret = question(1, fmt, args);
1311 va_end(args);
1313 return ret;
1316 int noyes(char *fmt, ...)
1318 int ret;
1319 va_list args;
1321 va_start(args, fmt);
1322 ret = question(0, fmt, args);
1323 va_end(args);
1325 return ret;
1328 int pm_printf(alpm_loglevel_t level, const char *format, ...)
1330 int ret;
1331 va_list args;
1333 /* print the message using va_arg list */
1334 va_start(args, format);
1335 ret = pm_vfprintf(stdout, level, format, args);
1336 va_end(args);
1338 return ret;
1341 int pm_fprintf(FILE *stream, alpm_loglevel_t level, const char *format, ...)
1343 int ret;
1344 va_list args;
1346 /* print the message using va_arg list */
1347 va_start(args, format);
1348 ret = pm_vfprintf(stream, level, format, args);
1349 va_end(args);
1351 return ret;
1354 int pm_asprintf(char **string, const char *format, ...)
1356 int ret = 0;
1357 va_list args;
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"));
1363 ret = -1;
1365 va_end(args);
1367 return ret;
1370 int pm_vasprintf(char **string, alpm_loglevel_t level, const char *format, va_list args)
1372 int ret = 0;
1373 char *msg = NULL;
1375 /* if current logmask does not overlap with level, do not print msg */
1376 if(!(config->logmask & level)) {
1377 return ret;
1380 /* print the message using va_arg list */
1381 ret = vasprintf(&msg, format, args);
1383 /* print a prefix to the message */
1384 switch(level) {
1385 case ALPM_LOG_ERROR:
1386 pm_asprintf(string, _("error: %s"), msg);
1387 break;
1388 case ALPM_LOG_WARNING:
1389 pm_asprintf(string, _("warning: %s"), msg);
1390 break;
1391 case ALPM_LOG_DEBUG:
1392 pm_asprintf(string, "debug: %s", msg);
1393 break;
1394 case ALPM_LOG_FUNCTION:
1395 pm_asprintf(string, "function: %s", msg);
1396 break;
1397 default:
1398 pm_asprintf(string, "%s", msg);
1399 break;
1401 free(msg);
1403 return ret;
1406 int pm_vfprintf(FILE *stream, alpm_loglevel_t level, const char *format, va_list args)
1408 int ret = 0;
1410 /* if current logmask does not overlap with level, do not print msg */
1411 if(!(config->logmask & level)) {
1412 return ret;
1415 #if defined(PACMAN_DEBUG)
1416 /* If debug is on, we'll timestamp the output */
1417 if(config->logmask & ALPM_LOG_DEBUG) {
1418 time_t t;
1419 struct tm *tmp;
1420 char timestr[10] = {0};
1422 t = time(NULL);
1423 tmp = localtime(&t);
1424 strftime(timestr, 9, "%H:%M:%S", tmp);
1425 timestr[8] = '\0';
1427 fprintf(stream, "[%s] ", timestr);
1429 #endif
1431 /* print a prefix to the message */
1432 switch(level) {
1433 case ALPM_LOG_ERROR:
1434 fprintf(stream, _("error: "));
1435 break;
1436 case ALPM_LOG_WARNING:
1437 fprintf(stream, _("warning: "));
1438 break;
1439 case ALPM_LOG_DEBUG:
1440 fprintf(stream, "debug: ");
1441 break;
1442 case ALPM_LOG_FUNCTION:
1443 fprintf(stream, "function: ");
1444 break;
1445 default:
1446 break;
1449 /* print the message using va_arg list */
1450 ret = vfprintf(stream, format, args);
1451 return ret;
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);
1460 return (p - s);
1463 char *strndup(const char *s, size_t n)
1465 size_t len = strnlen(s, n);
1466 char *new = (char *) malloc(len + 1);
1468 if(new == NULL)
1469 return NULL;
1471 new[len] = '\0';
1472 return (char *)memcpy(new, s, len);
1474 #endif
1476 /* vim: set ts=2 sw=2 noet: */