pacman-key: fix quotation on several variable assignments
[pacman-ng.git] / src / pacman / util.c
blob7065abdc80cc957e772935becf9c1a575bb50a09
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 enum _alpm_errno_t err = alpm_errno(config->handle);
67 pm_fprintf(stderr, ALPM_LOG_ERROR, _("failed to init transaction (%s)\n"),
68 alpm_strerror(err));
69 if(err == ALPM_ERR_HANDLE_LOCK) {
70 fprintf(stderr, _(" if you're sure a package manager is not already\n"
71 " running, you can remove %s\n"),
72 alpm_option_get_lockfile(config->handle));
75 return -1;
77 return 0;
80 int trans_release(void)
82 if(alpm_trans_release(config->handle) == -1) {
83 pm_fprintf(stderr, ALPM_LOG_ERROR, _("failed to release transaction (%s)\n"),
84 alpm_strerror(alpm_errno(config->handle)));
85 return -1;
87 return 0;
90 int needs_root(void)
92 switch(config->op) {
93 case PM_OP_DATABASE:
94 return 1;
95 case PM_OP_UPGRADE:
96 case PM_OP_REMOVE:
97 return !config->print;
98 case PM_OP_SYNC:
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));
102 default:
103 return 0;
107 int check_syncdbs(size_t need_repos, int check_valid)
109 int ret = 0;
110 alpm_list_t *i;
111 alpm_list_t *sync_dbs = alpm_option_get_syncdbs(config->handle);
113 if(need_repos && sync_dbs == NULL) {
114 pm_printf(ALPM_LOG_ERROR, _("no usable package repositories configured.\n"));
115 return 1;
118 if(check_valid) {
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)));
125 ret = 1;
129 return ret;
132 /* discard unhandled input on the terminal's input buffer */
133 static int flush_term_input(void) {
134 #ifdef HAVE_TCFLUSH
135 if(isatty(fileno(stdin))) {
136 return tcflush(fileno(stdin), TCIFLUSH);
138 #endif
140 /* fail silently */
141 return 0;
144 /* gets the current screen column width */
145 int getcols()
147 int termwidth = -1;
148 const int default_tty = 80;
149 const int default_notty = 0;
151 if(!isatty(fileno(stdout))) {
152 return default_notty;
155 #ifdef TIOCGSIZE
156 struct ttysize win;
157 if(ioctl(1, TIOCGSIZE, &win) == 0) {
158 termwidth = win.ts_cols;
160 #elif defined(TIOCGWINSZ)
161 struct winsize win;
162 if(ioctl(1, TIOCGWINSZ, &win) == 0) {
163 termwidth = win.ws_col;
165 #endif
166 return termwidth <= 0 ? default_tty : termwidth;
169 /* does the same thing as 'rm -rf' */
170 int rmrf(const char *path)
172 int errflag = 0;
173 struct dirent *dp;
174 DIR *dirp;
176 if(!unlink(path)) {
177 return 0;
178 } else {
179 if(errno == ENOENT) {
180 return 0;
181 } else if(errno == EPERM) {
182 /* fallthrough */
183 } else if(errno == EISDIR) {
184 /* fallthrough */
185 } else if(errno == ENOTDIR) {
186 return 1;
187 } else {
188 /* not a directory */
189 return 1;
192 dirp = opendir(path);
193 if(!dirp) {
194 return 1;
196 for(dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
197 if(dp->d_ino) {
198 char name[PATH_MAX];
199 sprintf(name, "%s/%s", path, dp->d_name);
200 if(strcmp(dp->d_name, "..") != 0 && strcmp(dp->d_name, ".") != 0) {
201 errflag += rmrf(name);
205 closedir(dirp);
206 if(rmdir(path)) {
207 errflag++;
209 return errflag;
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, '/');
221 if(last) {
222 return last + 1;
224 return 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)
235 char *ret, *last;
237 /* null or empty path */
238 if(path == NULL || path == '\0') {
239 return strdup(".");
242 ret = strdup(path);
243 last = strrchr(ret, '/');
245 if(last != NULL) {
246 /* we found a '/', so terminate our string */
247 *last = '\0';
248 return ret;
250 /* no slash found */
251 free(ret);
252 return strdup(".");
255 /* output a string, but wrap words properly with a specified indentation
257 void indentprint(const char *str, int indent)
259 wchar_t *wcstr;
260 const wchar_t *p;
261 int len, cidx;
262 const int cols = getcols();
264 if(!str) {
265 return;
268 /* if we're not a tty, or our tty is not wide enough that wrapping even makes
269 * sense, print without indenting */
270 if(cols == 0 || indent > cols) {
271 printf("%s", str);
272 return;
275 len = strlen(str) + 1;
276 wcstr = calloc(len, sizeof(wchar_t));
277 len = mbstowcs(wcstr, str, len);
278 p = wcstr;
279 cidx = indent;
281 if(!p || !len) {
282 return;
285 while(*p) {
286 if(*p == L' ') {
287 const wchar_t *q, *next;
288 p++;
289 if(p == NULL || *p == L' ') continue;
290 next = wcschr(p, L' ');
291 if(next == NULL) {
292 next = p + wcslen(p);
294 /* len captures # cols */
295 len = 0;
296 q = p;
297 while(q < next) {
298 len += wcwidth(*q++);
300 if(len > (cols - cidx - 1)) {
301 /* wrap to a newline and reindent */
302 printf("\n%-*s", indent, "");
303 cidx = indent;
304 } else {
305 printf(" ");
306 cidx++;
308 continue;
310 printf("%lc", (wint_t)*p);
311 cidx += wcwidth(*p);
312 p++;
314 free(wcstr);
317 /* Convert a string to uppercase
319 char *strtoupper(char *str)
321 char *ptr = str;
323 while(*ptr) {
324 (*ptr) = (char)toupper((unsigned char)*ptr);
325 ptr++;
327 return str;
330 /* Trim whitespace and newlines from a string
332 char *strtrim(char *str)
334 char *pch = str;
336 if(str == NULL || *str == '\0') {
337 /* string is empty, so we're done. */
338 return str;
341 while(isspace((unsigned char)*pch)) {
342 pch++;
344 if(pch != str) {
345 memmove(str, pch, (strlen(pch) + 1));
348 /* check if there wasn't anything but whitespace in the string. */
349 if(*str == '\0') {
350 return str;
353 pch = (str + (strlen(str) - 1));
354 while(isspace((unsigned char)*pch)) {
355 pch--;
357 *++pch = '\0';
359 return str;
362 /* Replace all occurances of 'needle' with 'replace' in 'str', returning
363 * a new string (must be free'd) */
364 char *strreplace(const char *str, const char *needle, const char *replace)
366 const char *p = NULL, *q = NULL;
367 char *newstr = NULL, *newp = NULL;
368 alpm_list_t *i = NULL, *list = NULL;
369 size_t needlesz = strlen(needle), replacesz = strlen(replace);
370 size_t newsz;
372 if(!str) {
373 return NULL;
376 p = str;
377 q = strstr(p, needle);
378 while(q) {
379 list = alpm_list_add(list, (char *)q);
380 p = q + needlesz;
381 q = strstr(p, needle);
384 /* no occurences of needle found */
385 if(!list) {
386 return strdup(str);
388 /* size of new string = size of old string + "number of occurences of needle"
389 * x "size difference between replace and needle" */
390 newsz = strlen(str) + 1 +
391 alpm_list_count(list) * (replacesz - needlesz);
392 newstr = calloc(newsz, sizeof(char));
393 if(!newstr) {
394 return NULL;
397 p = str;
398 newp = newstr;
399 for(i = list; i; i = alpm_list_next(i)) {
400 q = alpm_list_getdata(i);
401 if(q > p) {
402 /* add chars between this occurence and last occurence, if any */
403 memcpy(newp, p, (size_t)(q - p));
404 newp += q - p;
406 memcpy(newp, replace, replacesz);
407 newp += replacesz;
408 p = q + needlesz;
410 alpm_list_free(list);
412 if(*p) {
413 /* add the rest of 'p' */
414 strcpy(newp, p);
417 return newstr;
420 /** Splits a string into a list of strings using the chosen character as
421 * a delimiter.
423 * @param str the string to split
424 * @param splitchar the character to split at
426 * @return a list containing the duplicated strings
428 alpm_list_t *strsplit(const char *str, const char splitchar)
430 alpm_list_t *list = NULL;
431 const char *prev = str;
432 char *dup = NULL;
434 while((str = strchr(str, splitchar))) {
435 dup = strndup(prev, (size_t)(str - prev));
436 if(dup == NULL) {
437 return NULL;
439 list = alpm_list_add(list, dup);
441 str++;
442 prev = str;
445 dup = strdup(prev);
446 if(dup == NULL) {
447 return NULL;
449 list = alpm_list_add(list, dup);
451 return list;
454 static int string_length(const char *s)
456 int len;
457 wchar_t *wcstr;
459 if(!s) {
460 return 0;
462 /* len goes from # bytes -> # chars -> # cols */
463 len = strlen(s) + 1;
464 wcstr = calloc(len, sizeof(wchar_t));
465 len = mbstowcs(wcstr, s, len);
466 len = wcswidth(wcstr, len);
467 free(wcstr);
469 return len;
472 void string_display(const char *title, const char *string)
474 if(title) {
475 printf("%s ", title);
477 if(string == NULL || string[0] == '\0') {
478 printf(_("None"));
479 } else {
480 /* compute the length of title + a space */
481 int len = string_length(title) + 1;
482 indentprint(string, len);
484 printf("\n");
487 static void table_print_line(const alpm_list_t *line,
488 const alpm_list_t *formats)
490 const alpm_list_t *curformat = formats;
491 const alpm_list_t *curcell = line;
493 while(curcell && curformat) {
494 printf(alpm_list_getdata(curformat), alpm_list_getdata(curcell));
495 curcell = alpm_list_next(curcell);
496 curformat = alpm_list_next(curformat);
499 printf("\n");
502 /* creates format strings by checking max cell lengths in cols */
503 static alpm_list_t *table_create_format(const alpm_list_t *header,
504 const alpm_list_t *rows)
506 alpm_list_t *longest_str, *longest_strs = NULL;
507 alpm_list_t *formats = NULL;
508 const alpm_list_t *i, *row, *cell;
509 char *str, *formatstr;
510 const int padding = 2;
511 int colwidth, totalwidth = 0;
512 int curcol = 0;
514 /* header determines column count and initial values of longest_strs */
515 for(i = header; i; i = alpm_list_next(i)) {
516 longest_strs = alpm_list_add(longest_strs, alpm_list_getdata(i));
519 /* now find the longest string in each column */
520 for(longest_str = longest_strs; longest_str;
521 longest_str = alpm_list_next(longest_str), curcol++) {
522 for(i = rows; i; i = alpm_list_next(i)) {
523 row = alpm_list_getdata(i);
524 cell = alpm_list_nth(row, curcol);
525 str = alpm_list_getdata(cell);
527 if(strlen(str) > strlen(alpm_list_getdata(longest_str))) {
528 longest_str->data = str;
533 /* now use the column width info to generate format strings */
534 for(i = longest_strs; i; i = alpm_list_next(i)) {
535 const char *display;
536 colwidth = strlen(alpm_list_getdata(i)) + padding;
537 totalwidth += colwidth;
539 /* right align the last column for a cleaner table display */
540 display = (alpm_list_next(i) != NULL) ? "%%-%ds" : "%%%ds";
541 pm_asprintf(&formatstr, display, colwidth);
543 formats = alpm_list_add(formats, formatstr);
546 alpm_list_free(longest_strs);
548 /* return NULL if terminal is not wide enough */
549 if(totalwidth > getcols()) {
550 fprintf(stderr, _("insufficient columns available for table display\n"));
551 FREELIST(formats);
552 return NULL;
555 return formats;
558 /** Displays the list in table format
560 * @param title the tables title
561 * @param header the column headers. column count is determined by the nr
562 * of headers
563 * @param rows the rows to display as a list of lists of strings. the outer
564 * list represents the rows, the inner list the cells (= columns)
566 * @return -1 if not enough terminal cols available, else 0
568 int table_display(const char *title, const alpm_list_t *header,
569 const alpm_list_t *rows)
571 const alpm_list_t *i;
572 alpm_list_t *formats;
574 if(rows == NULL || header == NULL) {
575 return 0;
578 formats = table_create_format(header, rows);
579 if(formats == NULL) {
580 return -1;
583 if(title != NULL) {
584 printf("%s\n\n", title);
587 table_print_line(header, formats);
588 printf("\n");
590 for(i = rows; i; i = alpm_list_next(i)) {
591 table_print_line(alpm_list_getdata(i), formats);
594 FREELIST(formats);
595 return 0;
598 void list_display(const char *title, const alpm_list_t *list)
600 const alpm_list_t *i;
601 int len = 0;
603 if(title) {
604 len = string_length(title) + 1;
605 printf("%s ", title);
608 if(!list) {
609 printf("%s\n", _("None"));
610 } else {
611 const int maxcols = getcols();
612 int cols = len;
613 const char *str = alpm_list_getdata(list);
614 printf("%s", str);
615 cols += string_length(str);
616 for(i = alpm_list_next(list); i; i = alpm_list_next(i)) {
617 str = alpm_list_getdata(i);
618 int s = string_length(str);
619 /* wrap only if we have enough usable column space */
620 if(maxcols > len && cols + s + 2 >= maxcols) {
621 int j;
622 cols = len;
623 printf("\n");
624 for (j = 1; j <= len; j++) {
625 printf(" ");
627 } else if(cols != len) {
628 /* 2 spaces are added if this is not the first element on a line. */
629 printf(" ");
630 cols += 2;
632 printf("%s", str);
633 cols += s;
635 printf("\n");
639 void list_display_linebreak(const char *title, const alpm_list_t *list)
641 int len = 0;
643 if(title) {
644 len = string_length(title) + 1;
645 printf("%s ", title);
648 if(!list) {
649 printf("%s\n", _("None"));
650 } else {
651 const alpm_list_t *i;
652 /* Print the first element */
653 indentprint((const char *) alpm_list_getdata(list), len);
654 printf("\n");
655 /* Print the rest */
656 for(i = alpm_list_next(list); i; i = alpm_list_next(i)) {
657 int j;
658 for(j = 1; j <= len; j++) {
659 printf(" ");
661 indentprint((const char *) alpm_list_getdata(i), len);
662 printf("\n");
667 void signature_display(const char *title, alpm_sigresult_t *result)
669 int len = 0;
671 if(title) {
672 len = string_length(title) + 1;
673 printf("%s ", title);
675 if(result->count == 0) {
676 printf(_("None"));
677 } else {
678 int i;
679 for(i = 0; i < result->count; i++) {
680 char sigline[PATH_MAX];
681 const char *validity, *name;
682 /* Don't re-indent the first result */
683 if(i != 0) {
684 int j;
685 for(j = 1; j <= len; j++) {
686 printf(" ");
689 switch(result->status[i]) {
690 case ALPM_SIGSTATUS_VALID:
691 validity = _("Valid signature");
692 break;
693 case ALPM_SIGSTATUS_MARGINAL:
694 validity = _("Marginal signature");
695 break;
696 case ALPM_SIGSTATUS_UNKNOWN:
697 validity = _("Unknown signature");
698 break;
699 case ALPM_SIGSTATUS_BAD:
700 validity = _("Invalid signature");
701 break;
702 default:
703 validity = _("Signature error");
705 name = result->uid[i] ? result->uid[i] : _("<Key Unknown>");
706 snprintf(sigline, PATH_MAX, _("%s from \"%s\""), validity, name);
707 indentprint(sigline, len);
708 printf("\n");
713 /* creates a header row for use with table_display */
714 static alpm_list_t *create_verbose_header(int install)
716 alpm_list_t *res = NULL;
717 char *str;
719 pm_asprintf(&str, "%s", _("Name"));
720 res = alpm_list_add(res, str);
721 pm_asprintf(&str, "%s", _("Old Version"));
722 res = alpm_list_add(res, str);
723 if(install) {
724 pm_asprintf(&str, "%s", _("New Version"));
725 res = alpm_list_add(res, str);
727 pm_asprintf(&str, "%s", _("Size"));
728 res = alpm_list_add(res, str);
730 return res;
733 /* returns package info as list of strings */
734 static alpm_list_t *create_verbose_row(alpm_pkg_t *pkg, int install)
736 char *str;
737 double size;
738 const char *label;
739 alpm_list_t *ret = NULL;
740 alpm_db_t *ldb = alpm_option_get_localdb(config->handle);
742 /* a row consists of the package name, */
743 pm_asprintf(&str, "%s", alpm_pkg_get_name(pkg));
744 ret = alpm_list_add(ret, str);
746 /* old and new versions */
747 if(install) {
748 alpm_pkg_t *oldpkg = alpm_db_get_pkg(ldb, alpm_pkg_get_name(pkg));
749 pm_asprintf(&str, "%s",
750 oldpkg != NULL ? alpm_pkg_get_version(oldpkg) : "");
751 ret = alpm_list_add(ret, str);
754 pm_asprintf(&str, "%s", alpm_pkg_get_version(pkg));
755 ret = alpm_list_add(ret, str);
757 /* and size */
758 size = humanize_size(alpm_pkg_get_size(pkg), 'M', 1, &label);
759 pm_asprintf(&str, "%.2f %s", size, label);
760 ret = alpm_list_add(ret, str);
762 return ret;
765 /* prepare a list of pkgs to display */
766 void display_targets(const alpm_list_t *pkgs, int install)
768 char *str;
769 const char *title, *label;
770 double size;
771 const alpm_list_t *i;
772 off_t isize = 0, rsize = 0, dlsize = 0;
773 alpm_list_t *j, *lp, *header = NULL, *targets = NULL;
774 alpm_db_t *db_local = alpm_option_get_localdb(config->handle);
776 if(!pkgs) {
777 return;
780 /* gather pkg infos */
781 for(i = pkgs; i; i = alpm_list_next(i)) {
782 alpm_pkg_t *pkg = alpm_list_getdata(i);
784 if(install) {
785 alpm_pkg_t *lpkg = alpm_db_get_pkg(db_local, alpm_pkg_get_name(pkg));
786 dlsize += alpm_pkg_download_size(pkg);
787 if(lpkg) {
788 /* add up size of all removed packages */
789 rsize += alpm_pkg_get_isize(lpkg);
792 isize += alpm_pkg_get_isize(pkg);
794 if(config->verbosepkglists) {
795 targets = alpm_list_add(targets, create_verbose_row(pkg, install));
796 } else {
797 pm_asprintf(&str, "%s-%s", alpm_pkg_get_name(pkg),
798 alpm_pkg_get_version(pkg));
799 targets = alpm_list_add(targets, str);
803 /* print to screen */
804 title = install ? _("Targets (%d):") : _("Remove (%d):");
805 pm_asprintf(&str, title, alpm_list_count(pkgs));
807 printf("\n");
808 if(config->verbosepkglists) {
809 header = create_verbose_header(install);
810 if(table_display(str, header, targets) != 0) {
811 config->verbosepkglists = 0;
812 display_targets(pkgs, install);
813 goto out;
815 } else {
816 list_display(str, targets);
818 printf("\n");
820 if(install) {
821 size = humanize_size(dlsize, 'M', 1, &label);
822 printf(_("Total Download Size: %.2f %s\n"), size, label);
823 if(!(config->flags & ALPM_TRANS_FLAG_DOWNLOADONLY)) {
824 size = humanize_size(isize, 'M', 1, &label);
825 printf(_("Total Installed Size: %.2f %s\n"), size, label);
826 /* only show this net value if different from raw installed size */
827 if(rsize > 0) {
828 size = humanize_size(isize - rsize, 'M', 1, &label);
829 printf(_("Net Upgrade Size: %.2f %s\n"), size, label);
832 } else {
833 size = humanize_size(isize, 'M', 1, &label);
834 printf(_("Total Removed Size: %.2f %s\n"), size, label);
837 out:
838 /* cleanup */
839 if(config->verbosepkglists) {
840 /* targets is a list of lists of strings, free inner lists here */
841 for(j = targets; j; j = alpm_list_next(j)) {
842 lp = alpm_list_getdata(j);
843 FREELIST(lp);
845 alpm_list_free(targets);
846 FREELIST(header);
847 } else {
848 FREELIST(targets);
850 free(str);
853 static off_t pkg_get_size(alpm_pkg_t *pkg)
855 switch(config->op) {
856 case PM_OP_SYNC:
857 return alpm_pkg_download_size(pkg);
858 case PM_OP_UPGRADE:
859 return alpm_pkg_get_size(pkg);
860 default:
861 return alpm_pkg_get_isize(pkg);
865 static char *pkg_get_location(alpm_pkg_t *pkg)
867 alpm_list_t *servers;
868 char *string = NULL;
869 switch(config->op) {
870 case PM_OP_SYNC:
871 servers = alpm_db_get_servers(alpm_pkg_get_db(pkg));
872 if(servers) {
873 pm_asprintf(&string, "%s/%s", alpm_list_getdata(servers),
874 alpm_pkg_get_filename(pkg));
875 return string;
877 case PM_OP_UPGRADE:
878 return strdup(alpm_pkg_get_filename(pkg));
879 default:
880 pm_asprintf(&string, "%s-%s", alpm_pkg_get_name(pkg), alpm_pkg_get_version(pkg));
881 return string;
885 /** Converts sizes in bytes into human readable units.
887 * @param bytes the size in bytes
888 * @param target_unit '\0' or a short label. If equal to one of the short unit
889 * labels ('B', 'K', ...) bytes is converted to target_unit; if '\0', the first
890 * unit which will bring the value to below a threshold of 2048 will be chosen.
891 * @param long_labels whether to use short ("K") or long ("KiB") unit labels
892 * @param label will be set to the appropriate unit label
894 * @return the size in the appropriate unit
896 double humanize_size(off_t bytes, const char target_unit, int long_labels,
897 const char **label)
899 static const char *shortlabels[] = {"B", "K", "M", "G", "T", "P"};
900 static const char *longlabels[] = {"B", "KiB", "MiB", "GiB", "TiB", "PiB"};
901 static const int unitcount = sizeof(shortlabels) / sizeof(shortlabels[0]);
903 const char **labels = long_labels ? longlabels : shortlabels;
904 double val = (double)bytes;
905 int index;
907 for(index = 0; index < unitcount - 1; index++) {
908 if(target_unit != '\0' && shortlabels[index][0] == target_unit) {
909 break;
910 } else if(target_unit == '\0' && val <= 2048.0 && val >= -2048.0) {
911 break;
913 val /= 1024.0;
916 if(label) {
917 *label = labels[index];
920 return val;
923 void print_packages(const alpm_list_t *packages)
925 const alpm_list_t *i;
926 if(!config->print_format) {
927 config->print_format = strdup("%l");
929 for(i = packages; i; i = alpm_list_next(i)) {
930 alpm_pkg_t *pkg = alpm_list_getdata(i);
931 char *string = strdup(config->print_format);
932 char *temp = string;
933 /* %n : pkgname */
934 if(strstr(temp,"%n")) {
935 string = strreplace(temp, "%n", alpm_pkg_get_name(pkg));
936 free(temp);
937 temp = string;
939 /* %v : pkgver */
940 if(strstr(temp,"%v")) {
941 string = strreplace(temp, "%v", alpm_pkg_get_version(pkg));
942 free(temp);
943 temp = string;
945 /* %l : location */
946 if(strstr(temp,"%l")) {
947 char *pkgloc = pkg_get_location(pkg);
948 string = strreplace(temp, "%l", pkgloc);
949 free(pkgloc);
950 free(temp);
951 temp = string;
953 /* %r : repo */
954 if(strstr(temp,"%r")) {
955 const char *repo = "local";
956 alpm_db_t *db = alpm_pkg_get_db(pkg);
957 if(db) {
958 repo = alpm_db_get_name(db);
960 string = strreplace(temp, "%r", repo);
961 free(temp);
962 temp = string;
964 /* %s : size */
965 if(strstr(temp,"%s")) {
966 char *size;
967 pm_asprintf(&size, "%jd", (intmax_t)pkg_get_size(pkg));
968 string = strreplace(temp, "%s", size);
969 free(size);
970 free(temp);
972 printf("%s\n",string);
973 free(string);
977 /* Helper function for comparing strings using the
978 * alpm "compare func" signature */
979 int str_cmp(const void *s1, const void *s2)
981 return strcmp(s1, s2);
984 void display_new_optdepends(alpm_pkg_t *oldpkg, alpm_pkg_t *newpkg)
986 alpm_list_t *old = alpm_pkg_get_optdepends(oldpkg);
987 alpm_list_t *new = alpm_pkg_get_optdepends(newpkg);
988 alpm_list_t *optdeps = alpm_list_diff(new,old,str_cmp);
989 if(optdeps) {
990 printf(_("New optional dependencies for %s\n"), alpm_pkg_get_name(newpkg));
991 list_display_linebreak(" ", optdeps);
993 alpm_list_free(optdeps);
996 void display_optdepends(alpm_pkg_t *pkg)
998 alpm_list_t *optdeps = alpm_pkg_get_optdepends(pkg);
999 if(optdeps) {
1000 printf(_("Optional dependencies for %s\n"), alpm_pkg_get_name(pkg));
1001 list_display_linebreak(" ", optdeps);
1005 static void display_repo_list(const char *dbname, alpm_list_t *list)
1007 const char *prefix= " ";
1009 printf(":: ");
1010 printf(_("Repository %s\n"), dbname);
1011 list_display(prefix, list);
1014 void select_display(const alpm_list_t *pkglist)
1016 const alpm_list_t *i;
1017 int nth = 1;
1018 alpm_list_t *list = NULL;
1019 char *string = NULL;
1020 const char *dbname = NULL;
1022 for (i = pkglist; i; i = i->next) {
1023 alpm_pkg_t *pkg = alpm_list_getdata(i);
1024 alpm_db_t *db = alpm_pkg_get_db(pkg);
1026 if(!dbname)
1027 dbname = alpm_db_get_name(db);
1028 if(strcmp(alpm_db_get_name(db), dbname) != 0) {
1029 display_repo_list(dbname, list);
1030 FREELIST(list);
1031 dbname = alpm_db_get_name(db);
1033 string = NULL;
1034 pm_asprintf(&string, "%d) %s", nth, alpm_pkg_get_name(pkg));
1035 list = alpm_list_add(list, string);
1036 nth++;
1038 display_repo_list(dbname, list);
1039 FREELIST(list);
1042 static int parseindex(char *s, int *val, int min, int max)
1044 char *endptr = NULL;
1045 int n = strtol(s, &endptr, 10);
1046 if(*endptr == '\0') {
1047 if(n < min || n > max) {
1048 fprintf(stderr, _("Invalid value: %d is not between %d and %d\n"),
1049 n, min, max);
1050 return -1;
1052 *val = n;
1053 return 0;
1054 } else {
1055 fprintf(stderr, _("Invalid number: %s\n"), s);
1056 return -1;
1060 static int multiselect_parse(char *array, int count, char *response)
1062 char *str, *saveptr;
1064 for (str = response; ; str = NULL) {
1065 int include = 1;
1066 int start, end;
1067 char *ends = NULL;
1068 char *starts = strtok_r(str, " ", &saveptr);
1070 if(starts == NULL) {
1071 break;
1073 strtrim(starts);
1074 int len = strlen(starts);
1075 if(len == 0)
1076 continue;
1078 if(*starts == '^') {
1079 starts++;
1080 len--;
1081 include = 0;
1082 } else if(str) {
1083 /* if first token is including, we unselect all targets */
1084 memset(array, 0, count);
1087 if(len > 1) {
1088 /* check for range */
1089 char *p;
1090 if((p = strchr(starts + 1, '-'))) {
1091 *p = 0;
1092 ends = p + 1;
1096 if(parseindex(starts, &start, 1, count) != 0)
1097 return -1;
1099 if(!ends) {
1100 array[start-1] = include;
1101 } else {
1102 int d;
1103 if(parseindex(ends, &end, start, count) != 0) {
1104 return -1;
1106 for(d = start; d <= end; d++) {
1107 array[d-1] = include;
1112 return 0;
1115 int multiselect_question(char *array, int count)
1117 char response[64];
1118 FILE *stream;
1120 if(config->noconfirm) {
1121 stream = stdout;
1122 } else {
1123 /* Use stderr so questions are always displayed when redirecting output */
1124 stream = stderr;
1127 while(1) {
1128 memset(array, 1, count);
1130 fprintf(stream, "\n");
1131 fprintf(stream, _("Enter a selection (default=all)"));
1132 fprintf(stream, ": ");
1134 if(config->noconfirm) {
1135 fprintf(stream, "\n");
1136 break;
1139 flush_term_input();
1141 if(fgets(response, sizeof(response), stdin)) {
1142 strtrim(response);
1143 if(strlen(response) > 0) {
1144 if(multiselect_parse(array, count, response) == -1) {
1145 /* only loop if user gave an invalid answer */
1146 continue;
1150 break;
1152 return 0;
1155 int select_question(int count)
1157 char response[32];
1158 FILE *stream;
1159 int preset = 1;
1161 if(config->noconfirm) {
1162 stream = stdout;
1163 } else {
1164 /* Use stderr so questions are always displayed when redirecting output */
1165 stream = stderr;
1168 while(1) {
1169 fprintf(stream, "\n");
1170 fprintf(stream, _("Enter a number (default=%d)"), preset);
1171 fprintf(stream, ": ");
1173 if(config->noconfirm) {
1174 fprintf(stream, "\n");
1175 break;
1178 flush_term_input();
1180 if(fgets(response, sizeof(response), stdin)) {
1181 strtrim(response);
1182 if(strlen(response) > 0) {
1183 int n;
1184 if(parseindex(response, &n, 1, count) != 0)
1185 continue;
1186 return (n - 1);
1189 break;
1192 return (preset - 1);
1196 /* presents a prompt and gets a Y/N answer */
1197 static int question(short preset, char *fmt, va_list args)
1199 char response[32];
1200 FILE *stream;
1202 if(config->noconfirm) {
1203 stream = stdout;
1204 } else {
1205 /* Use stderr so questions are always displayed when redirecting output */
1206 stream = stderr;
1209 /* ensure all text makes it to the screen before we prompt the user */
1210 fflush(stdout);
1211 fflush(stderr);
1213 vfprintf(stream, fmt, args);
1215 if(preset) {
1216 fprintf(stream, " %s ", _("[Y/n]"));
1217 } else {
1218 fprintf(stream, " %s ", _("[y/N]"));
1221 if(config->noconfirm) {
1222 fprintf(stream, "\n");
1223 return preset;
1226 fflush(stream);
1227 flush_term_input();
1229 if(fgets(response, sizeof(response), stdin)) {
1230 strtrim(response);
1231 if(strlen(response) == 0) {
1232 return preset;
1235 if(strcasecmp(response, _("Y")) == 0 || strcasecmp(response, _("YES")) == 0) {
1236 return 1;
1237 } else if(strcasecmp(response, _("N")) == 0 || strcasecmp(response, _("NO")) == 0) {
1238 return 0;
1241 return 0;
1244 int yesno(char *fmt, ...)
1246 int ret;
1247 va_list args;
1249 va_start(args, fmt);
1250 ret = question(1, fmt, args);
1251 va_end(args);
1253 return ret;
1256 int noyes(char *fmt, ...)
1258 int ret;
1259 va_list args;
1261 va_start(args, fmt);
1262 ret = question(0, fmt, args);
1263 va_end(args);
1265 return ret;
1268 int pm_printf(alpm_loglevel_t level, const char *format, ...)
1270 int ret;
1271 va_list args;
1273 /* print the message using va_arg list */
1274 va_start(args, format);
1275 ret = pm_vfprintf(stdout, level, format, args);
1276 va_end(args);
1278 return ret;
1281 int pm_fprintf(FILE *stream, alpm_loglevel_t level, const char *format, ...)
1283 int ret;
1284 va_list args;
1286 /* print the message using va_arg list */
1287 va_start(args, format);
1288 ret = pm_vfprintf(stream, level, format, args);
1289 va_end(args);
1291 return ret;
1294 int pm_asprintf(char **string, const char *format, ...)
1296 int ret = 0;
1297 va_list args;
1299 /* print the message using va_arg list */
1300 va_start(args, format);
1301 if(vasprintf(string, format, args) == -1) {
1302 pm_fprintf(stderr, ALPM_LOG_ERROR, _("failed to allocate string\n"));
1303 ret = -1;
1305 va_end(args);
1307 return ret;
1310 int pm_vasprintf(char **string, alpm_loglevel_t level, const char *format, va_list args)
1312 int ret = 0;
1313 char *msg = NULL;
1315 /* if current logmask does not overlap with level, do not print msg */
1316 if(!(config->logmask & level)) {
1317 return ret;
1320 /* print the message using va_arg list */
1321 ret = vasprintf(&msg, format, args);
1323 /* print a prefix to the message */
1324 switch(level) {
1325 case ALPM_LOG_ERROR:
1326 pm_asprintf(string, _("error: %s"), msg);
1327 break;
1328 case ALPM_LOG_WARNING:
1329 pm_asprintf(string, _("warning: %s"), msg);
1330 break;
1331 case ALPM_LOG_DEBUG:
1332 pm_asprintf(string, "debug: %s", msg);
1333 break;
1334 case ALPM_LOG_FUNCTION:
1335 pm_asprintf(string, "function: %s", msg);
1336 break;
1337 default:
1338 pm_asprintf(string, "%s", msg);
1339 break;
1341 free(msg);
1343 return ret;
1346 int pm_vfprintf(FILE *stream, alpm_loglevel_t level, const char *format, va_list args)
1348 int ret = 0;
1350 /* if current logmask does not overlap with level, do not print msg */
1351 if(!(config->logmask & level)) {
1352 return ret;
1355 #if defined(PACMAN_DEBUG)
1356 /* If debug is on, we'll timestamp the output */
1357 if(config->logmask & ALPM_LOG_DEBUG) {
1358 time_t t;
1359 struct tm *tmp;
1360 char timestr[10] = {0};
1362 t = time(NULL);
1363 tmp = localtime(&t);
1364 strftime(timestr, 9, "%H:%M:%S", tmp);
1365 timestr[8] = '\0';
1367 printf("[%s] ", timestr);
1369 #endif
1371 /* print a prefix to the message */
1372 switch(level) {
1373 case ALPM_LOG_ERROR:
1374 fprintf(stream, _("error: "));
1375 break;
1376 case ALPM_LOG_WARNING:
1377 fprintf(stream, _("warning: "));
1378 break;
1379 case ALPM_LOG_DEBUG:
1380 fprintf(stream, "debug: ");
1381 break;
1382 case ALPM_LOG_FUNCTION:
1383 fprintf(stream, "function: ");
1384 break;
1385 default:
1386 break;
1389 /* print the message using va_arg list */
1390 ret = vfprintf(stream, format, args);
1391 return ret;
1394 #ifndef HAVE_STRNDUP
1395 /* A quick and dirty implementation derived from glibc */
1396 static size_t strnlen(const char *s, size_t max)
1398 register const char *p;
1399 for(p = s; *p && max--; ++p);
1400 return (p - s);
1403 char *strndup(const char *s, size_t n)
1405 size_t len = strnlen(s, n);
1406 char *new = (char *) malloc(len + 1);
1408 if(new == NULL)
1409 return NULL;
1411 new[len] = '\0';
1412 return (char *)memcpy(new, s, len);
1414 #endif
1416 /* vim: set ts=2 sw=2 noet: */