Rename pmtransflag_t to alpm_transflag_t
[pacman-ng.git] / src / pacman / util.c
bloba661b2e9eccafc84ad07712e3729eb016bf31b9c
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)
54 int ret;
55 if(config->print) {
56 ret = alpm_trans_init(config->handle, flags, NULL, NULL, NULL);
57 } else {
58 ret = alpm_trans_init(config->handle, flags, cb_trans_evt, cb_trans_conv,
59 cb_trans_progress);
62 if(ret == -1) {
63 enum _pmerrno_t err = alpm_errno(config->handle);
64 pm_fprintf(stderr, PM_LOG_ERROR, _("failed to init transaction (%s)\n"),
65 alpm_strerror(err));
66 if(err == PM_ERR_HANDLE_LOCK) {
67 fprintf(stderr, _(" if you're sure a package manager is not already\n"
68 " running, you can remove %s\n"),
69 alpm_option_get_lockfile(config->handle));
72 return -1;
74 return 0;
77 int trans_release(void)
79 if(alpm_trans_release(config->handle) == -1) {
80 pm_fprintf(stderr, PM_LOG_ERROR, _("failed to release transaction (%s)\n"),
81 alpm_strerror(alpm_errno(config->handle)));
82 return -1;
84 return 0;
87 int needs_root(void)
89 switch(config->op) {
90 case PM_OP_DATABASE:
91 return 1;
92 case PM_OP_UPGRADE:
93 case PM_OP_REMOVE:
94 return !config->print;
95 case PM_OP_SYNC:
96 return (config->op_s_clean || config->op_s_sync ||
97 (!config->group && !config->op_s_info && !config->op_q_list &&
98 !config->op_s_search && !config->print));
99 default:
100 return 0;
104 /* discard unhandled input on the terminal's input buffer */
105 static int flush_term_input(void) {
106 #ifdef HAVE_TCFLUSH
107 if(isatty(fileno(stdin))) {
108 return tcflush(fileno(stdin), TCIFLUSH);
110 #endif
112 /* fail silently */
113 return 0;
116 /* gets the current screen column width */
117 int getcols()
119 int termwidth = -1;
120 const int default_tty = 80;
121 const int default_notty = 0;
123 if(!isatty(fileno(stdout))) {
124 return default_notty;
127 #ifdef TIOCGSIZE
128 struct ttysize win;
129 if(ioctl(1, TIOCGSIZE, &win) == 0) {
130 termwidth = win.ts_cols;
132 #elif defined(TIOCGWINSZ)
133 struct winsize win;
134 if(ioctl(1, TIOCGWINSZ, &win) == 0) {
135 termwidth = win.ws_col;
137 #endif
138 return termwidth <= 0 ? default_tty : termwidth;
141 /* does the same thing as 'rm -rf' */
142 int rmrf(const char *path)
144 int errflag = 0;
145 struct dirent *dp;
146 DIR *dirp;
148 if(!unlink(path)) {
149 return 0;
150 } else {
151 if(errno == ENOENT) {
152 return 0;
153 } else if(errno == EPERM) {
154 /* fallthrough */
155 } else if(errno == EISDIR) {
156 /* fallthrough */
157 } else if(errno == ENOTDIR) {
158 return 1;
159 } else {
160 /* not a directory */
161 return 1;
164 dirp = opendir(path);
165 if(!dirp) {
166 return 1;
168 for(dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
169 if(dp->d_ino) {
170 char name[PATH_MAX];
171 sprintf(name, "%s/%s", path, dp->d_name);
172 if(strcmp(dp->d_name, "..") != 0 && strcmp(dp->d_name, ".") != 0) {
173 errflag += rmrf(name);
177 closedir(dirp);
178 if(rmdir(path)) {
179 errflag++;
181 return errflag;
185 /** Parse the basename of a program from a path.
186 * @param path path to parse basename from
188 * @return everything following the final '/'
190 const char *mbasename(const char *path)
192 const char *last = strrchr(path, '/');
193 if(last) {
194 return last + 1;
196 return path;
199 /** Parse the dirname of a program from a path.
200 * The path returned should be freed.
201 * @param path path to parse dirname from
203 * @return everything preceding the final '/'
205 char *mdirname(const char *path)
207 char *ret, *last;
209 /* null or empty path */
210 if(path == NULL || path == '\0') {
211 return strdup(".");
214 ret = strdup(path);
215 last = strrchr(ret, '/');
217 if(last != NULL) {
218 /* we found a '/', so terminate our string */
219 *last = '\0';
220 return ret;
222 /* no slash found */
223 free(ret);
224 return strdup(".");
227 /* output a string, but wrap words properly with a specified indentation
229 void indentprint(const char *str, int indent)
231 wchar_t *wcstr;
232 const wchar_t *p;
233 int len, cidx;
234 const int cols = getcols();
236 if(!str) {
237 return;
240 /* if we're not a tty, or our tty is not wide enough that wrapping even makes
241 * sense, print without indenting */
242 if(cols == 0 || indent > cols) {
243 printf("%s", str);
244 return;
247 len = strlen(str) + 1;
248 wcstr = calloc(len, sizeof(wchar_t));
249 len = mbstowcs(wcstr, str, len);
250 p = wcstr;
251 cidx = indent;
253 if(!p || !len) {
254 return;
257 while(*p) {
258 if(*p == L' ') {
259 const wchar_t *q, *next;
260 p++;
261 if(p == NULL || *p == L' ') continue;
262 next = wcschr(p, L' ');
263 if(next == NULL) {
264 next = p + wcslen(p);
266 /* len captures # cols */
267 len = 0;
268 q = p;
269 while(q < next) {
270 len += wcwidth(*q++);
272 if(len > (cols - cidx - 1)) {
273 /* wrap to a newline and reindent */
274 printf("\n%-*s", indent, "");
275 cidx = indent;
276 } else {
277 printf(" ");
278 cidx++;
280 continue;
282 printf("%lc", (wint_t)*p);
283 cidx += wcwidth(*p);
284 p++;
286 free(wcstr);
289 /* Convert a string to uppercase
291 char *strtoupper(char *str)
293 char *ptr = str;
295 while(*ptr) {
296 (*ptr) = (char)toupper((unsigned char)*ptr);
297 ptr++;
299 return str;
302 /* Trim whitespace and newlines from a string
304 char *strtrim(char *str)
306 char *pch = str;
308 if(str == NULL || *str == '\0') {
309 /* string is empty, so we're done. */
310 return str;
313 while(isspace((unsigned char)*pch)) {
314 pch++;
316 if(pch != str) {
317 memmove(str, pch, (strlen(pch) + 1));
320 /* check if there wasn't anything but whitespace in the string. */
321 if(*str == '\0') {
322 return str;
325 pch = (str + (strlen(str) - 1));
326 while(isspace((unsigned char)*pch)) {
327 pch--;
329 *++pch = '\0';
331 return str;
334 /* Replace all occurances of 'needle' with 'replace' in 'str', returning
335 * a new string (must be free'd) */
336 char *strreplace(const char *str, const char *needle, const char *replace)
338 const char *p = NULL, *q = NULL;
339 char *newstr = NULL, *newp = NULL;
340 alpm_list_t *i = NULL, *list = NULL;
341 size_t needlesz = strlen(needle), replacesz = strlen(replace);
342 size_t newsz;
344 if(!str) {
345 return NULL;
348 p = str;
349 q = strstr(p, needle);
350 while(q) {
351 list = alpm_list_add(list, (char *)q);
352 p = q + needlesz;
353 q = strstr(p, needle);
356 /* no occurences of needle found */
357 if(!list) {
358 return strdup(str);
360 /* size of new string = size of old string + "number of occurences of needle"
361 * x "size difference between replace and needle" */
362 newsz = strlen(str) + 1 +
363 alpm_list_count(list) * (replacesz - needlesz);
364 newstr = malloc(newsz);
365 if(!newstr) {
366 return NULL;
368 *newstr = '\0';
370 p = str;
371 newp = newstr;
372 for(i = list; i; i = alpm_list_next(i)) {
373 q = alpm_list_getdata(i);
374 if(q > p){
375 /* add chars between this occurence and last occurence, if any */
376 strncpy(newp, p, (size_t)(q - p));
377 newp += q - p;
379 strncpy(newp, replace, replacesz);
380 newp += replacesz;
381 p = q + needlesz;
383 alpm_list_free(list);
385 if(*p) {
386 /* add the rest of 'p' */
387 strcpy(newp, p);
388 newp += strlen(p);
390 *newp = '\0';
392 return newstr;
395 /** Splits a string into a list of strings using the chosen character as
396 * a delimiter.
398 * @param str the string to split
399 * @param splitchar the character to split at
401 * @return a list containing the duplicated strings
403 alpm_list_t *strsplit(const char *str, const char splitchar)
405 alpm_list_t *list = NULL;
406 const char *prev = str;
407 char *dup = NULL;
409 while((str = strchr(str, splitchar))) {
410 dup = strndup(prev, (size_t)(str - prev));
411 if(dup == NULL) {
412 return NULL;
414 list = alpm_list_add(list, dup);
416 str++;
417 prev = str;
420 dup = strdup(prev);
421 if(dup == NULL) {
422 return NULL;
424 list = alpm_list_add(list, dup);
426 return list;
429 static int string_length(const char *s)
431 int len;
432 wchar_t *wcstr;
434 if(!s) {
435 return 0;
437 /* len goes from # bytes -> # chars -> # cols */
438 len = strlen(s) + 1;
439 wcstr = calloc(len, sizeof(wchar_t));
440 len = mbstowcs(wcstr, s, len);
441 len = wcswidth(wcstr, len);
442 free(wcstr);
444 return len;
447 void string_display(const char *title, const char *string)
449 if(title) {
450 printf("%s ", title);
452 if(string == NULL || string[0] == '\0') {
453 printf(_("None"));
454 } else {
455 /* compute the length of title + a space */
456 int len = string_length(title) + 1;
457 indentprint(string, len);
459 printf("\n");
462 static void table_print_line(const alpm_list_t *line,
463 const alpm_list_t *formats)
465 const alpm_list_t *curformat = formats;
466 const alpm_list_t *curcell = line;
468 while(curcell && curformat) {
469 printf(alpm_list_getdata(curformat), alpm_list_getdata(curcell));
470 curcell = alpm_list_next(curcell);
471 curformat = alpm_list_next(curformat);
474 printf("\n");
477 /* creates format strings by checking max cell lengths in cols */
478 static alpm_list_t *table_create_format(const alpm_list_t *header,
479 const alpm_list_t *rows)
481 alpm_list_t *longest_str, *longest_strs = NULL;
482 alpm_list_t *formats = NULL;
483 const alpm_list_t *i, *row, *cell;
484 char *str, *formatstr;
485 const int padding = 2;
486 int colwidth, totalwidth = 0;
487 int curcol = 0;
489 /* header determines column count and initial values of longest_strs */
490 for(i = header; i; i = alpm_list_next(i)) {
491 longest_strs = alpm_list_add(longest_strs, alpm_list_getdata(i));
494 /* now find the longest string in each column */
495 for(longest_str = longest_strs; longest_str;
496 longest_str = alpm_list_next(longest_str), curcol++) {
497 for(i = rows; i; i = alpm_list_next(i)) {
498 row = alpm_list_getdata(i);
499 cell = alpm_list_nth(row, curcol);
500 str = alpm_list_getdata(cell);
502 if(strlen(str) > strlen(alpm_list_getdata(longest_str))) {
503 longest_str->data = str;
508 /* now use the column width info to generate format strings */
509 for(i = longest_strs; i; i = alpm_list_next(i)) {
510 const char *display;
511 colwidth = strlen(alpm_list_getdata(i)) + padding;
512 totalwidth += colwidth;
514 /* right align the last column for a cleaner table display */
515 display = (alpm_list_next(i) != NULL) ? "%%-%ds" : "%%%ds";
516 pm_asprintf(&formatstr, display, colwidth);
518 formats = alpm_list_add(formats, formatstr);
521 alpm_list_free(longest_strs);
523 /* return NULL if terminal is not wide enough */
524 if(totalwidth > getcols()) {
525 fprintf(stderr, _("insufficient columns available for table display\n"));
526 FREELIST(formats);
527 return NULL;
530 return formats;
533 /** Displays the list in table format
535 * @param title the tables title
536 * @param header the column headers. column count is determined by the nr
537 * of headers
538 * @param rows the rows to display as a list of lists of strings. the outer
539 * list represents the rows, the inner list the cells (= columns)
541 * @return -1 if not enough terminal cols available, else 0
543 int table_display(const char *title, const alpm_list_t *header,
544 const alpm_list_t *rows)
546 const alpm_list_t *i;
547 alpm_list_t *formats;
549 if(rows == NULL || header == NULL) {
550 return 0;
553 formats = table_create_format(header, rows);
554 if(formats == NULL) {
555 return -1;
558 if(title != NULL) {
559 printf("%s\n\n", title);
562 table_print_line(header, formats);
563 printf("\n");
565 for(i = rows; i; i = alpm_list_next(i)) {
566 table_print_line(alpm_list_getdata(i), formats);
569 FREELIST(formats);
570 return 0;
573 void list_display(const char *title, const alpm_list_t *list)
575 const alpm_list_t *i;
576 int len = 0;
578 if(title) {
579 len = string_length(title) + 1;
580 printf("%s ", title);
583 if(!list) {
584 printf("%s\n", _("None"));
585 } else {
586 const int maxcols = getcols();
587 int cols = len;
588 const char *str = alpm_list_getdata(list);
589 printf("%s", str);
590 cols += string_length(str);
591 for(i = alpm_list_next(list); i; i = alpm_list_next(i)) {
592 str = alpm_list_getdata(i);
593 int s = string_length(str);
594 /* wrap only if we have enough usable column space */
595 if(maxcols > len && cols + s + 2 >= maxcols) {
596 int j;
597 cols = len;
598 printf("\n");
599 for (j = 1; j <= len; j++) {
600 printf(" ");
602 } else if(cols != len) {
603 /* 2 spaces are added if this is not the first element on a line. */
604 printf(" ");
605 cols += 2;
607 printf("%s", str);
608 cols += s;
610 printf("\n");
614 void list_display_linebreak(const char *title, const alpm_list_t *list)
616 const alpm_list_t *i;
617 int len = 0;
619 if(title) {
620 len = string_length(title) + 1;
621 printf("%s ", title);
624 if(!list) {
625 printf("%s\n", _("None"));
626 } else {
627 /* Print the first element */
628 indentprint((const char *) alpm_list_getdata(list), len);
629 printf("\n");
630 /* Print the rest */
631 for(i = alpm_list_next(list); i; i = alpm_list_next(i)) {
632 int j;
633 for(j = 1; j <= len; j++) {
634 printf(" ");
636 indentprint((const char *) alpm_list_getdata(i), len);
637 printf("\n");
642 /* creates a header row for use with table_display */
643 static alpm_list_t *create_verbose_header(int install)
645 alpm_list_t *res = NULL;
646 char *str;
648 pm_asprintf(&str, "%s", _("Name"));
649 res = alpm_list_add(res, str);
650 pm_asprintf(&str, "%s", _("Old Version"));
651 res = alpm_list_add(res, str);
652 if(install) {
653 pm_asprintf(&str, "%s", _("New Version"));
654 res = alpm_list_add(res, str);
656 pm_asprintf(&str, "%s", _("Size"));
657 res = alpm_list_add(res, str);
659 return res;
662 /* returns package info as list of strings */
663 static alpm_list_t *create_verbose_row(alpm_pkg_t *pkg, int install)
665 char *str;
666 double size;
667 const char *label;
668 alpm_list_t *ret = NULL;
669 alpm_db_t *ldb = alpm_option_get_localdb(config->handle);
671 /* a row consists of the package name, */
672 pm_asprintf(&str, "%s", alpm_pkg_get_name(pkg));
673 ret = alpm_list_add(ret, str);
675 /* old and new versions */
676 if(install) {
677 alpm_pkg_t *oldpkg = alpm_db_get_pkg(ldb, alpm_pkg_get_name(pkg));
678 pm_asprintf(&str, "%s",
679 oldpkg != NULL ? alpm_pkg_get_version(oldpkg) : "");
680 ret = alpm_list_add(ret, str);
683 pm_asprintf(&str, "%s", alpm_pkg_get_version(pkg));
684 ret = alpm_list_add(ret, str);
686 /* and size */
687 size = humanize_size(alpm_pkg_get_size(pkg), 'M', 1, &label);
688 pm_asprintf(&str, "%.2f %s", size, label);
689 ret = alpm_list_add(ret, str);
691 return ret;
694 /* prepare a list of pkgs to display */
695 void display_targets(const alpm_list_t *pkgs, int install)
697 char *str;
698 const char *title, *label;
699 double size;
700 const alpm_list_t *i;
701 off_t isize = 0, rsize = 0, dlsize = 0;
702 alpm_list_t *j, *lp, *header = NULL, *targets = NULL;
703 alpm_db_t *db_local = alpm_option_get_localdb(config->handle);
705 if(!pkgs) {
706 return;
709 /* gather pkg infos */
710 for(i = pkgs; i; i = alpm_list_next(i)) {
711 alpm_pkg_t *pkg = alpm_list_getdata(i);
713 if(install) {
714 alpm_pkg_t *lpkg = alpm_db_get_pkg(db_local, alpm_pkg_get_name(pkg));
715 dlsize += alpm_pkg_download_size(pkg);
716 if(lpkg) {
717 /* add up size of all removed packages */
718 rsize += alpm_pkg_get_isize(lpkg);
721 isize += alpm_pkg_get_isize(pkg);
723 if(config->verbosepkglists) {
724 targets = alpm_list_add(targets, create_verbose_row(pkg, install));
725 } else {
726 pm_asprintf(&str, "%s-%s", alpm_pkg_get_name(pkg),
727 alpm_pkg_get_version(pkg));
728 targets = alpm_list_add(targets, str);
732 /* print to screen */
733 title = install ? _("Targets (%d):") : _("Remove (%d):");
734 pm_asprintf(&str, title, alpm_list_count(pkgs));
736 printf("\n");
737 if(config->verbosepkglists) {
738 header = create_verbose_header(install);
739 if(table_display(str, header, targets) != 0) {
740 config->verbosepkglists = 0;
741 display_targets(pkgs, install);
742 goto out;
744 } else {
745 list_display(str, targets);
747 printf("\n");
749 if(install) {
750 size = humanize_size(dlsize, 'M', 1, &label);
751 printf(_("Total Download Size: %.2f %s\n"), size, label);
752 if(!(config->flags & PM_TRANS_FLAG_DOWNLOADONLY)) {
753 size = humanize_size(isize, 'M', 1, &label);
754 printf(_("Total Installed Size: %.2f %s\n"), size, label);
755 /* only show this net value if different from raw installed size */
756 if(rsize > 0) {
757 size = humanize_size(isize - rsize, 'M', 1, &label);
758 printf(_("Net Upgrade Size: %.2f %s\n"), size, label);
761 } else {
762 size = humanize_size(isize, 'M', 1, &label);
763 printf(_("Total Removed Size: %.2f %s\n"), size, label);
766 out:
767 /* cleanup */
768 if(config->verbosepkglists) {
769 /* targets is a list of lists of strings, free inner lists here */
770 for(j = alpm_list_first(targets); j; j = alpm_list_next(j)) {
771 lp = alpm_list_getdata(j);
772 FREELIST(lp);
774 alpm_list_free(targets);
775 FREELIST(header);
776 } else {
777 FREELIST(targets);
779 free(str);
782 static off_t pkg_get_size(alpm_pkg_t *pkg)
784 switch(config->op) {
785 case PM_OP_SYNC:
786 return alpm_pkg_download_size(pkg);
787 case PM_OP_UPGRADE:
788 return alpm_pkg_get_size(pkg);
789 default:
790 return alpm_pkg_get_isize(pkg);
794 static char *pkg_get_location(alpm_pkg_t *pkg)
796 alpm_list_t *servers;
797 char *string = NULL;
798 switch(config->op) {
799 case PM_OP_SYNC:
800 servers = alpm_db_get_servers(alpm_pkg_get_db(pkg));
801 if(servers) {
802 pm_asprintf(&string, "%s/%s", alpm_list_getdata(servers),
803 alpm_pkg_get_filename(pkg));
804 return string;
806 case PM_OP_UPGRADE:
807 return strdup(alpm_pkg_get_filename(pkg));
808 default:
809 pm_asprintf(&string, "%s-%s", alpm_pkg_get_name(pkg), alpm_pkg_get_version(pkg));
810 return string;
814 /** Converts sizes in bytes into human readable units.
816 * @param bytes the size in bytes
817 * @param target_unit '\0' or a short label. If equal to one of the short unit
818 * labels ('B', 'K', ...) bytes is converted to target_unit; if '\0', the first
819 * unit which will bring the value to below a threshold of 2048 will be chosen.
820 * @param long_labels whether to use short ("K") or long ("KiB") unit labels
821 * @param label will be set to the appropriate unit label
823 * @return the size in the appropriate unit
825 double humanize_size(off_t bytes, const char target_unit, int long_labels,
826 const char **label)
828 static const char *shortlabels[] = {"B", "K", "M", "G", "T", "P"};
829 static const char *longlabels[] = {"B", "KiB", "MiB", "GiB", "TiB", "PiB"};
830 static const int unitcount = sizeof(shortlabels) / sizeof(shortlabels[0]);
832 const char **labels = long_labels ? longlabels : shortlabels;
833 double val = (double)bytes;
834 int index;
836 for(index = 0; index < unitcount - 1; index++) {
837 if(target_unit != '\0' && shortlabels[index][0] == target_unit) {
838 break;
839 } else if(target_unit == '\0' && val <= 2048.0 && val >= -2048.0) {
840 break;
842 val /= 1024.0;
845 if(label) {
846 *label = labels[index];
849 return val;
852 void print_packages(const alpm_list_t *packages)
854 const alpm_list_t *i;
855 if(!config->print_format) {
856 config->print_format = strdup("%l");
858 for(i = packages; i; i = alpm_list_next(i)) {
859 alpm_pkg_t *pkg = alpm_list_getdata(i);
860 char *string = strdup(config->print_format);
861 char *temp = string;
862 /* %n : pkgname */
863 if(strstr(temp,"%n")) {
864 string = strreplace(temp, "%n", alpm_pkg_get_name(pkg));
865 free(temp);
866 temp = string;
868 /* %v : pkgver */
869 if(strstr(temp,"%v")) {
870 string = strreplace(temp, "%v", alpm_pkg_get_version(pkg));
871 free(temp);
872 temp = string;
874 /* %l : location */
875 if(strstr(temp,"%l")) {
876 char *pkgloc = pkg_get_location(pkg);
877 string = strreplace(temp, "%l", pkgloc);
878 free(pkgloc);
879 free(temp);
880 temp = string;
882 /* %r : repo */
883 if(strstr(temp,"%r")) {
884 const char *repo = "local";
885 alpm_db_t *db = alpm_pkg_get_db(pkg);
886 if(db) {
887 repo = alpm_db_get_name(db);
889 string = strreplace(temp, "%r", repo);
890 free(temp);
891 temp = string;
893 /* %s : size */
894 if(strstr(temp,"%s")) {
895 char *size;
896 pm_asprintf(&size, "%jd", (intmax_t)pkg_get_size(pkg));
897 string = strreplace(temp, "%s", size);
898 free(size);
899 free(temp);
901 printf("%s\n",string);
902 free(string);
906 /* Helper function for comparing strings using the
907 * alpm "compare func" signature */
908 int str_cmp(const void *s1, const void *s2)
910 return strcmp(s1, s2);
913 void display_new_optdepends(alpm_pkg_t *oldpkg, alpm_pkg_t *newpkg)
915 alpm_list_t *old = alpm_pkg_get_optdepends(oldpkg);
916 alpm_list_t *new = alpm_pkg_get_optdepends(newpkg);
917 alpm_list_t *optdeps = alpm_list_diff(new,old,str_cmp);
918 if(optdeps) {
919 printf(_("New optional dependencies for %s\n"), alpm_pkg_get_name(newpkg));
920 list_display_linebreak(" ", optdeps);
922 alpm_list_free(optdeps);
925 void display_optdepends(alpm_pkg_t *pkg)
927 alpm_list_t *optdeps = alpm_pkg_get_optdepends(pkg);
928 if(optdeps) {
929 printf(_("Optional dependencies for %s\n"), alpm_pkg_get_name(pkg));
930 list_display_linebreak(" ", optdeps);
934 static void display_repo_list(const char *dbname, alpm_list_t *list)
936 const char *prefix= " ";
938 printf(":: ");
939 printf(_("Repository %s\n"), dbname);
940 list_display(prefix, list);
943 void select_display(const alpm_list_t *pkglist)
945 const alpm_list_t *i;
946 int nth = 1;
947 alpm_list_t *list = NULL;
948 char *string = NULL;
949 const char *dbname = NULL;
951 for (i = pkglist; i; i = i->next) {
952 alpm_pkg_t *pkg = alpm_list_getdata(i);
953 alpm_db_t *db = alpm_pkg_get_db(pkg);
955 if(!dbname)
956 dbname = alpm_db_get_name(db);
957 if(strcmp(alpm_db_get_name(db), dbname) != 0) {
958 display_repo_list(dbname, list);
959 FREELIST(list);
960 dbname = alpm_db_get_name(db);
962 string = NULL;
963 pm_asprintf(&string, "%d) %s", nth, alpm_pkg_get_name(pkg));
964 list = alpm_list_add(list, string);
965 nth++;
967 display_repo_list(dbname, list);
968 FREELIST(list);
971 static int parseindex(char *s, int *val, int min, int max)
973 char *endptr = NULL;
974 int n = strtol(s, &endptr, 10);
975 if(*endptr == '\0') {
976 if(n < min || n > max) {
977 fprintf(stderr, _("Invalid value: %d is not between %d and %d\n"),
978 n, min, max);
979 return -1;
981 *val = n;
982 return 0;
983 } else {
984 fprintf(stderr, _("Invalid number: %s\n"), s);
985 return -1;
989 static int multiselect_parse(char *array, int count, char *response)
991 char *str, *saveptr;
993 for (str = response; ; str = NULL) {
994 int include = 1;
995 int start, end;
996 char *ends = NULL;
997 char *starts = strtok_r(str, " ", &saveptr);
999 if(starts == NULL) {
1000 break;
1002 strtrim(starts);
1003 int len = strlen(starts);
1004 if(len == 0)
1005 continue;
1007 if(*starts == '^') {
1008 starts++;
1009 len--;
1010 include = 0;
1011 } else if(str) {
1012 /* if first token is including, we unselect all targets */
1013 memset(array, 0, count);
1016 if(len > 1) {
1017 /* check for range */
1018 char *p;
1019 if((p = strchr(starts + 1, '-'))) {
1020 *p = 0;
1021 ends = p + 1;
1025 if(parseindex(starts, &start, 1, count) != 0)
1026 return -1;
1028 if(!ends) {
1029 array[start-1] = include;
1030 } else {
1031 int d;
1032 if(parseindex(ends, &end, start, count) != 0) {
1033 return -1;
1035 for(d = start; d <= end; d++) {
1036 array[d-1] = include;
1041 return 0;
1044 int multiselect_question(char *array, int count)
1046 char response[64];
1047 FILE *stream;
1049 if(config->noconfirm) {
1050 stream = stdout;
1051 } else {
1052 /* Use stderr so questions are always displayed when redirecting output */
1053 stream = stderr;
1056 while(1) {
1057 memset(array, 1, count);
1059 fprintf(stream, "\n");
1060 fprintf(stream, _("Enter a selection (default=all)"));
1061 fprintf(stream, ": ");
1063 if(config->noconfirm) {
1064 fprintf(stream, "\n");
1065 break;
1068 flush_term_input();
1070 if(fgets(response, sizeof(response), stdin)) {
1071 strtrim(response);
1072 if(strlen(response) > 0) {
1073 if(multiselect_parse(array, count, response) == -1) {
1074 /* only loop if user gave an invalid answer */
1075 continue;
1079 break;
1081 return 0;
1084 int select_question(int count)
1086 char response[32];
1087 FILE *stream;
1088 int preset = 1;
1090 if(config->noconfirm) {
1091 stream = stdout;
1092 } else {
1093 /* Use stderr so questions are always displayed when redirecting output */
1094 stream = stderr;
1097 while(1) {
1098 fprintf(stream, "\n");
1099 fprintf(stream, _("Enter a number (default=%d)"), preset);
1100 fprintf(stream, ": ");
1102 if(config->noconfirm) {
1103 fprintf(stream, "\n");
1104 break;
1107 flush_term_input();
1109 if(fgets(response, sizeof(response), stdin)) {
1110 strtrim(response);
1111 if(strlen(response) > 0) {
1112 int n;
1113 if(parseindex(response, &n, 1, count) != 0)
1114 continue;
1115 return (n - 1);
1118 break;
1121 return (preset - 1);
1125 /* presents a prompt and gets a Y/N answer */
1126 static int question(short preset, char *fmt, va_list args)
1128 char response[32];
1129 FILE *stream;
1131 if(config->noconfirm) {
1132 stream = stdout;
1133 } else {
1134 /* Use stderr so questions are always displayed when redirecting output */
1135 stream = stderr;
1138 /* ensure all text makes it to the screen before we prompt the user */
1139 fflush(stdout);
1140 fflush(stderr);
1142 vfprintf(stream, fmt, args);
1144 if(preset) {
1145 fprintf(stream, " %s ", _("[Y/n]"));
1146 } else {
1147 fprintf(stream, " %s ", _("[y/N]"));
1150 if(config->noconfirm) {
1151 fprintf(stream, "\n");
1152 return preset;
1155 fflush(stream);
1156 flush_term_input();
1158 if(fgets(response, sizeof(response), stdin)) {
1159 strtrim(response);
1160 if(strlen(response) == 0) {
1161 return preset;
1164 if(strcasecmp(response, _("Y")) == 0 || strcasecmp(response, _("YES")) == 0) {
1165 return 1;
1166 } else if(strcasecmp(response, _("N")) == 0 || strcasecmp(response, _("NO")) == 0) {
1167 return 0;
1170 return 0;
1173 int yesno(char *fmt, ...)
1175 int ret;
1176 va_list args;
1178 va_start(args, fmt);
1179 ret = question(1, fmt, args);
1180 va_end(args);
1182 return ret;
1185 int noyes(char *fmt, ...)
1187 int ret;
1188 va_list args;
1190 va_start(args, fmt);
1191 ret = question(0, fmt, args);
1192 va_end(args);
1194 return ret;
1197 int pm_printf(alpm_loglevel_t level, const char *format, ...)
1199 int ret;
1200 va_list args;
1202 /* print the message using va_arg list */
1203 va_start(args, format);
1204 ret = pm_vfprintf(stdout, level, format, args);
1205 va_end(args);
1207 return ret;
1210 int pm_fprintf(FILE *stream, alpm_loglevel_t level, const char *format, ...)
1212 int ret;
1213 va_list args;
1215 /* print the message using va_arg list */
1216 va_start(args, format);
1217 ret = pm_vfprintf(stream, level, format, args);
1218 va_end(args);
1220 return ret;
1223 int pm_asprintf(char **string, const char *format, ...)
1225 int ret = 0;
1226 va_list args;
1228 /* print the message using va_arg list */
1229 va_start(args, format);
1230 if(vasprintf(string, format, args) == -1) {
1231 pm_fprintf(stderr, PM_LOG_ERROR, _("failed to allocate string\n"));
1232 ret = -1;
1234 va_end(args);
1236 return ret;
1239 int pm_vasprintf(char **string, alpm_loglevel_t level, const char *format, va_list args)
1241 int ret = 0;
1242 char *msg = NULL;
1244 /* if current logmask does not overlap with level, do not print msg */
1245 if(!(config->logmask & level)) {
1246 return ret;
1249 /* print the message using va_arg list */
1250 ret = vasprintf(&msg, format, args);
1252 /* print a prefix to the message */
1253 switch(level) {
1254 case PM_LOG_ERROR:
1255 pm_asprintf(string, _("error: %s"), msg);
1256 break;
1257 case PM_LOG_WARNING:
1258 pm_asprintf(string, _("warning: %s"), msg);
1259 break;
1260 case PM_LOG_DEBUG:
1261 pm_asprintf(string, "debug: %s", msg);
1262 break;
1263 case PM_LOG_FUNCTION:
1264 pm_asprintf(string, "function: %s", msg);
1265 break;
1266 default:
1267 pm_asprintf(string, "%s", msg);
1268 break;
1270 free(msg);
1272 return ret;
1275 int pm_vfprintf(FILE *stream, alpm_loglevel_t level, const char *format, va_list args)
1277 int ret = 0;
1279 /* if current logmask does not overlap with level, do not print msg */
1280 if(!(config->logmask & level)) {
1281 return ret;
1284 #if defined(PACMAN_DEBUG)
1285 /* If debug is on, we'll timestamp the output */
1286 if(config->logmask & PM_LOG_DEBUG) {
1287 time_t t;
1288 struct tm *tmp;
1289 char timestr[10] = {0};
1291 t = time(NULL);
1292 tmp = localtime(&t);
1293 strftime(timestr, 9, "%H:%M:%S", tmp);
1294 timestr[8] = '\0';
1296 printf("[%s] ", timestr);
1298 #endif
1300 /* print a prefix to the message */
1301 switch(level) {
1302 case PM_LOG_ERROR:
1303 fprintf(stream, _("error: "));
1304 break;
1305 case PM_LOG_WARNING:
1306 fprintf(stream, _("warning: "));
1307 break;
1308 case PM_LOG_DEBUG:
1309 fprintf(stream, "debug: ");
1310 break;
1311 case PM_LOG_FUNCTION:
1312 fprintf(stream, "function: ");
1313 break;
1314 default:
1315 break;
1318 /* print the message using va_arg list */
1319 ret = vfprintf(stream, format, args);
1320 return ret;
1323 #ifndef HAVE_STRNDUP
1324 /* A quick and dirty implementation derived from glibc */
1325 static size_t strnlen(const char *s, size_t max)
1327 register const char *p;
1328 for(p = s; *p && max--; ++p);
1329 return (p - s);
1332 char *strndup(const char *s, size_t n)
1334 size_t len = strnlen(s, n);
1335 char *new = (char *) malloc(len + 1);
1337 if(new == NULL)
1338 return NULL;
1340 new[len] = '\0';
1341 return (char *)memcpy(new, s, len);
1343 #endif
1345 /* vim: set ts=2 sw=2 noet: */