fix a few warnings reported by clang
[pacman-ng.git] / src / pacman / util.c
blobe421e9d5330ad1ffca7143f4e7a3e3329c5f6af9
1 /*
2 * util.c
4 * Copyright (c) 2006-2009 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/time.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <stdarg.h>
30 #include <string.h>
31 #include <errno.h>
32 #include <fcntl.h>
33 #include <ctype.h>
34 #include <dirent.h>
35 #include <unistd.h>
36 #include <limits.h>
37 #include <wchar.h>
39 #include <alpm.h>
40 #include <alpm_list.h>
42 /* pacman */
43 #include "util.h"
44 #include "conf.h"
45 #include "callback.h"
48 int trans_init(pmtransflag_t flags)
50 int ret;
51 if(config->print) {
52 ret = alpm_trans_init(flags, NULL, NULL, NULL);
53 } else {
54 ret = alpm_trans_init(flags, cb_trans_evt, cb_trans_conv,
55 cb_trans_progress);
58 if(ret == -1) {
59 pm_fprintf(stderr, PM_LOG_ERROR, _("failed to init transaction (%s)\n"),
60 alpm_strerrorlast());
61 if(pm_errno == PM_ERR_HANDLE_LOCK) {
62 fprintf(stderr, _(" if you're sure a package manager is not already\n"
63 " running, you can remove %s\n"), alpm_option_get_lockfile());
65 return(-1);
67 return(0);
70 int trans_release(void)
72 if(alpm_trans_release() == -1) {
73 pm_fprintf(stderr, PM_LOG_ERROR, _("failed to release transaction (%s)\n"),
74 alpm_strerrorlast());
75 return(-1);
77 return(0);
80 int needs_root(void)
82 switch(config->op) {
83 case PM_OP_UPGRADE:
84 case PM_OP_REMOVE:
85 return(!config->print);
86 case PM_OP_SYNC:
87 return(config->op_s_clean || config->op_s_sync ||
88 (!config->group && !config->op_s_info && !config->op_q_list &&
89 !config->op_s_search && !config->print));
90 default:
91 return(0);
95 /* gets the current screen column width */
96 int getcols(void)
98 #ifdef TIOCGSIZE
99 struct ttysize win;
100 if(ioctl(1, TIOCGSIZE, &win) == 0) {
101 return win.ts_cols;
103 #elif defined(TIOCGWINSZ)
104 struct winsize win;
105 if(ioctl(1, TIOCGWINSZ, &win) == 0) {
106 return win.ws_col;
108 #endif
109 return 0;
112 /* does the same thing as 'rm -rf' */
113 int rmrf(const char *path)
115 int errflag = 0;
116 struct dirent *dp;
117 DIR *dirp;
119 if(!unlink(path)) {
120 return(0);
121 } else {
122 if(errno == ENOENT) {
123 return(0);
124 } else if(errno == EPERM) {
125 /* fallthrough */
126 } else if(errno == EISDIR) {
127 /* fallthrough */
128 } else if(errno == ENOTDIR) {
129 return(1);
130 } else {
131 /* not a directory */
132 return(1);
135 dirp = opendir(path);
136 if(!dirp) {
137 return(1);
139 for(dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
140 if(dp->d_ino) {
141 char name[PATH_MAX];
142 sprintf(name, "%s/%s", path, dp->d_name);
143 if(strcmp(dp->d_name, "..") && strcmp(dp->d_name, ".")) {
144 errflag += rmrf(name);
148 closedir(dirp);
149 if(rmdir(path)) {
150 errflag++;
152 return(errflag);
156 /** Parse the basename of a program from a path.
157 * Grabbed from the uClibc source.
158 * @param path path to parse basename from
160 * @return everything following the final '/'
162 char *mbasename(const char *path)
164 const char *s;
165 const char *p;
167 p = s = path;
169 while (*s) {
170 if (*s++ == '/') {
171 p = s;
175 return (char *)p;
178 /** Parse the dirname of a program from a path.
179 * The path returned should be freed.
180 * @param path path to parse dirname from
182 * @return everything preceding the final '/'
184 char *mdirname(const char *path)
186 char *ret, *last;
188 /* null or empty path */
189 if(path == NULL || path == '\0') {
190 return(strdup("."));
193 ret = strdup(path);
194 last = strrchr(ret, '/');
196 if(last != NULL) {
197 /* we found a '/', so terminate our string */
198 *last = '\0';
199 return(ret);
201 /* no slash found */
202 free(ret);
203 return(strdup("."));
206 /* output a string, but wrap words properly with a specified indentation
208 void indentprint(const char *str, int indent)
210 wchar_t *wcstr;
211 const wchar_t *p;
212 int len, cidx, cols;
214 if(!str) {
215 return;
218 cols = getcols();
220 /* if we're not a tty, print without indenting */
221 if(cols == 0) {
222 printf("%s", str);
223 return;
226 len = strlen(str) + 1;
227 wcstr = calloc(len, sizeof(wchar_t));
228 len = mbstowcs(wcstr, str, len);
229 p = wcstr;
230 cidx = indent;
232 if(!p || !len) {
233 return;
236 while(*p) {
237 if(*p == L' ') {
238 const wchar_t *q, *next;
239 p++;
240 if(p == NULL || *p == L' ') continue;
241 next = wcschr(p, L' ');
242 if(next == NULL) {
243 next = p + wcslen(p);
245 /* len captures # cols */
246 len = 0;
247 q = p;
248 while(q < next) {
249 len += wcwidth(*q++);
251 if(len > (cols - cidx - 1)) {
252 /* wrap to a newline and reindent */
253 printf("\n%-*s", indent, "");
254 cidx = indent;
255 } else {
256 printf(" ");
257 cidx++;
259 continue;
261 printf("%lc", (wint_t)*p);
262 cidx += wcwidth(*p);
263 p++;
265 free(wcstr);
268 /* Convert a string to uppercase
270 char *strtoupper(char *str)
272 char *ptr = str;
274 while(*ptr) {
275 (*ptr) = toupper((unsigned char)*ptr);
276 ptr++;
278 return str;
281 /* Trim whitespace and newlines from a string
283 char *strtrim(char *str)
285 char *pch = str;
287 if(str == NULL || *str == '\0') {
288 /* string is empty, so we're done. */
289 return(str);
292 while(isspace((unsigned char)*pch)) {
293 pch++;
295 if(pch != str) {
296 memmove(str, pch, (strlen(pch) + 1));
299 /* check if there wasn't anything but whitespace in the string. */
300 if(*str == '\0') {
301 return(str);
304 pch = (str + (strlen(str) - 1));
305 while(isspace((unsigned char)*pch)) {
306 pch--;
308 *++pch = '\0';
310 return(str);
313 /* Replace all occurances of 'needle' with 'replace' in 'str', returning
314 * a new string (must be free'd) */
315 char *strreplace(const char *str, const char *needle, const char *replace)
317 const char *p = NULL, *q = NULL;
318 char *newstr = NULL, *newp = NULL;
319 alpm_list_t *i = NULL, *list = NULL;
320 size_t needlesz = strlen(needle), replacesz = strlen(replace);
321 size_t newsz;
323 if(!str) {
324 return(NULL);
327 p = str;
328 q = strstr(p, needle);
329 while(q) {
330 list = alpm_list_add(list, (char *)q);
331 p = q + needlesz;
332 q = strstr(p, needle);
335 /* no occurences of needle found */
336 if(!list) {
337 return(strdup(str));
339 /* size of new string = size of old string + "number of occurences of needle"
340 * x "size difference between replace and needle" */
341 newsz = strlen(str) + 1 +
342 alpm_list_count(list) * (replacesz - needlesz);
343 newstr = malloc(newsz);
344 if(!newstr) {
345 return(NULL);
347 *newstr = '\0';
349 p = str;
350 newp = newstr;
351 for(i = list; i; i = alpm_list_next(i)) {
352 q = alpm_list_getdata(i);
353 if(q > p){
354 /* add chars between this occurence and last occurence, if any */
355 strncpy(newp, p, q - p);
356 newp += q - p;
358 strncpy(newp, replace, replacesz);
359 newp += replacesz;
360 p = q + needlesz;
362 alpm_list_free(list);
364 if(*p) {
365 /* add the rest of 'p' */
366 strcpy(newp, p);
367 newp += strlen(p);
369 *newp = '\0';
371 return(newstr);
374 /** Splits a string into a list of strings using the chosen character as
375 * a delimiter.
377 * @param str the string to split
378 * @param splitchar the character to split at
380 * @return a list containing the duplicated strings
382 alpm_list_t *strsplit(const char *str, const char splitchar)
384 alpm_list_t *list = NULL;
385 const char *prev = str;
386 char *dup = NULL;
388 while((str = strchr(str, splitchar))) {
389 dup = strndup(prev, str - prev);
390 if(dup == NULL) {
391 return(NULL);
393 list = alpm_list_add(list, dup);
395 str++;
396 prev = str;
399 dup = strdup(prev);
400 if(dup == NULL) {
401 return(NULL);
403 list = alpm_list_add(list, dup);
405 return(list);
408 static int string_length(const char *s)
410 int len;
411 wchar_t *wcstr;
413 if(!s) {
414 return(0);
416 /* len goes from # bytes -> # chars -> # cols */
417 len = strlen(s) + 1;
418 wcstr = calloc(len, sizeof(wchar_t));
419 len = mbstowcs(wcstr, s, len);
420 len = wcswidth(wcstr, len);
421 free(wcstr);
423 return(len);
426 void string_display(const char *title, const char *string)
428 int len = 0;
430 if(title) {
431 printf("%s ", title);
433 if(string == NULL || string[0] == '\0') {
434 printf(_("None"));
435 } else {
436 /* compute the length of title + a space */
437 len = string_length(title) + 1;
438 indentprint(string, len);
440 printf("\n");
443 void list_display(const char *title, const alpm_list_t *list)
445 const alpm_list_t *i;
446 int cols, len = 0;
448 if(title) {
449 len = string_length(title) + 1;
450 printf("%s ", title);
453 if(!list) {
454 printf("%s\n", _("None"));
455 } else {
456 for(i = list, cols = len; i; i = alpm_list_next(i)) {
457 char *str = alpm_list_getdata(i);
458 int s = string_length(str);
459 int maxcols = getcols();
460 if(maxcols > 0 && (cols + s + 2) >= maxcols) {
461 int j;
462 cols = len;
463 printf("\n");
464 for (j = 1; j <= len; j++) {
465 printf(" ");
467 } else if (cols != len) {
468 /* 2 spaces are added if this is not the first element on a line. */
469 printf(" ");
470 cols += 2;
472 printf("%s", str);
473 cols += s;
475 printf("\n");
479 void list_display_linebreak(const char *title, const alpm_list_t *list)
481 const alpm_list_t *i;
482 int len = 0;
484 if(title) {
485 len = string_length(title) + 1;
486 printf("%s ", title);
489 if(!list) {
490 printf("%s\n", _("None"));
491 } else {
492 /* Print the first element */
493 indentprint((const char *) alpm_list_getdata(list), len);
494 printf("\n");
495 /* Print the rest */
496 for(i = alpm_list_next(list); i; i = alpm_list_next(i)) {
497 int j;
498 for(j = 1; j <= len; j++) {
499 printf(" ");
501 indentprint((const char *) alpm_list_getdata(i), len);
502 printf("\n");
506 /* prepare a list of pkgs to display */
507 void display_targets(const alpm_list_t *pkgs, int install)
509 char *str;
510 const alpm_list_t *i;
511 off_t isize = 0, dlsize = 0;
512 double mbisize = 0.0, mbdlsize = 0.0;
513 alpm_list_t *targets = NULL;
515 if(!pkgs) {
516 return;
519 printf("\n");
520 for(i = pkgs; i; i = alpm_list_next(i)) {
521 pmpkg_t *pkg = alpm_list_getdata(i);
523 dlsize += alpm_pkg_download_size(pkg);
524 isize += alpm_pkg_get_isize(pkg);
526 /* print the package size with the output if ShowSize option set */
527 if(config->showsize) {
528 double mbsize = 0.0;
529 mbsize = alpm_pkg_get_size(pkg) / (1024.0 * 1024.0);
531 asprintf(&str, "%s-%s [%.2f MB]", alpm_pkg_get_name(pkg),
532 alpm_pkg_get_version(pkg), mbsize);
533 } else {
534 asprintf(&str, "%s-%s", alpm_pkg_get_name(pkg),
535 alpm_pkg_get_version(pkg));
537 targets = alpm_list_add(targets, str);
540 /* Convert byte sizes to MB */
541 mbdlsize = dlsize / (1024.0 * 1024.0);
542 mbisize = isize / (1024.0 * 1024.0);
544 if(install) {
545 asprintf(&str, _("Targets (%d):"), alpm_list_count(targets));
546 list_display(str, targets);
547 free(str);
548 printf("\n");
550 printf(_("Total Download Size: %.2f MB\n"), mbdlsize);
551 if(!(config->flags & PM_TRANS_FLAG_DOWNLOADONLY)) {
552 printf(_("Total Installed Size: %.2f MB\n"), mbisize);
554 } else {
555 asprintf(&str, _("Remove (%d):"), alpm_list_count(targets));
556 list_display(str, targets);
557 free(str);
558 printf("\n");
560 printf(_("Total Removed Size: %.2f MB\n"), mbisize);
563 FREELIST(targets);
566 static off_t pkg_get_size(pmpkg_t *pkg)
568 switch(config->op) {
569 case PM_OP_SYNC:
570 return(alpm_pkg_download_size(pkg));
571 case PM_OP_UPGRADE:
572 return(alpm_pkg_get_size(pkg));
573 default:
574 return(alpm_pkg_get_isize(pkg));
578 static char *pkg_get_location(pmpkg_t *pkg)
580 pmdb_t *db;
581 const char *dburl;
582 char *string;
583 switch(config->op) {
584 case PM_OP_SYNC:
585 db = alpm_pkg_get_db(pkg);
586 dburl = alpm_db_get_url(db);
587 if(dburl) {
588 char *pkgurl = NULL;
589 asprintf(&pkgurl, "%s/%s", dburl, alpm_pkg_get_filename(pkg));
590 return(pkgurl);
592 case PM_OP_UPGRADE:
593 return(strdup(alpm_pkg_get_filename(pkg)));
594 default:
595 string = NULL;
596 asprintf(&string, "%s-%s", alpm_pkg_get_name(pkg), alpm_pkg_get_version(pkg));
597 return(string);
601 void print_packages(const alpm_list_t *packages)
603 const alpm_list_t *i;
604 if(!config->print_format) {
605 config->print_format = strdup("%l");
607 for(i = packages; i; i = alpm_list_next(i)) {
608 pmpkg_t *pkg = alpm_list_getdata(i);
609 char *string = strdup(config->print_format);
610 char *temp = string;
611 /* %n : pkgname */
612 if(strstr(temp,"%n")) {
613 string = strreplace(temp, "%n", alpm_pkg_get_name(pkg));
614 free(temp);
615 temp = string;
617 /* %v : pkgver */
618 if(strstr(temp,"%v")) {
619 string = strreplace(temp, "%v", alpm_pkg_get_version(pkg));
620 free(temp);
621 temp = string;
623 /* %l : location */
624 if(strstr(temp,"%l")) {
625 char *pkgloc = pkg_get_location(pkg);
626 string = strreplace(temp, "%l", pkgloc);
627 free(pkgloc);
628 free(temp);
629 temp = string;
631 /* %r : repo */
632 if(strstr(temp,"%r")) {
633 const char *repo = "local";
634 pmdb_t *db = alpm_pkg_get_db(pkg);
635 if(db) {
636 repo = alpm_db_get_name(db);
638 string = strreplace(temp, "%r", repo);
639 free(temp);
640 temp = string;
642 /* %s : size */
643 if(strstr(temp,"%s")) {
644 char *size;
645 double mbsize = 0.0;
646 mbsize = pkg_get_size(pkg) / (1024.0 * 1024.0);
647 asprintf(&size, "%.2f", mbsize);
648 string = strreplace(temp, "%s", size);
649 free(size);
650 free(temp);
651 temp = string;
653 printf("%s\n",string);
654 free(string);
658 /* Helper function for comparing strings using the
659 * alpm "compare func" signature */
660 int str_cmp(const void *s1, const void *s2)
662 return(strcmp(s1, s2));
665 void display_new_optdepends(pmpkg_t *oldpkg, pmpkg_t *newpkg)
667 alpm_list_t *old = alpm_pkg_get_optdepends(oldpkg);
668 alpm_list_t *new = alpm_pkg_get_optdepends(newpkg);
669 alpm_list_t *optdeps = alpm_list_diff(new,old,str_cmp);
670 if(optdeps) {
671 printf(_("New optional dependencies for %s\n"), alpm_pkg_get_name(newpkg));
672 list_display_linebreak(" ", optdeps);
674 alpm_list_free(optdeps);
677 void display_optdepends(pmpkg_t *pkg)
679 alpm_list_t *optdeps = alpm_pkg_get_optdepends(pkg);
680 if(optdeps) {
681 printf(_("Optional dependencies for %s\n"), alpm_pkg_get_name(pkg));
682 list_display_linebreak(" ", optdeps);
686 /* presents a prompt and gets a Y/N answer */
687 static int question(short preset, char *fmt, va_list args)
689 char response[32];
690 int sresponse = sizeof(response)-1;
691 FILE *stream;
693 if(config->noconfirm) {
694 stream = stdout;
695 } else {
696 /* Use stderr so questions are always displayed when redirecting output */
697 stream = stderr;
700 vfprintf(stream, fmt, args);
702 if(preset) {
703 fprintf(stream, " %s ", _("[Y/n]"));
704 } else {
705 fprintf(stream, " %s ", _("[y/N]"));
708 if(config->noconfirm) {
709 fprintf(stream, "\n");
710 return(preset);
713 if(fgets(response, sresponse, stdin)) {
714 strtrim(response);
715 if(strlen(response) == 0) {
716 return(preset);
719 if(!strcasecmp(response, _("Y")) || !strcasecmp(response, _("YES"))) {
720 return(1);
721 } else if (!strcasecmp(response, _("N")) || !strcasecmp(response, _("NO"))) {
722 return(0);
725 return(0);
728 int yesno(char *fmt, ...)
730 int ret;
731 va_list args;
733 va_start(args, fmt);
734 ret = question(1, fmt, args);
735 va_end(args);
737 return(ret);
740 int noyes(char *fmt, ...)
742 int ret;
743 va_list args;
745 va_start(args, fmt);
746 ret = question(0, fmt, args);
747 va_end(args);
749 return(ret);
752 int pm_printf(pmloglevel_t level, const char *format, ...)
754 int ret;
755 va_list args;
757 /* print the message using va_arg list */
758 va_start(args, format);
759 ret = pm_vfprintf(stdout, level, format, args);
760 va_end(args);
762 return(ret);
765 int pm_fprintf(FILE *stream, pmloglevel_t level, const char *format, ...)
767 int ret;
768 va_list args;
770 /* print the message using va_arg list */
771 va_start(args, format);
772 ret = pm_vfprintf(stream, level, format, args);
773 va_end(args);
775 return(ret);
778 int pm_vasprintf(char **string, pmloglevel_t level, const char *format, va_list args)
780 int ret = 0;
781 char *msg = NULL;
783 /* if current logmask does not overlap with level, do not print msg */
784 if(!(config->logmask & level)) {
785 return ret;
788 /* print the message using va_arg list */
789 ret = vasprintf(&msg, format, args);
791 /* print a prefix to the message */
792 switch(level) {
793 case PM_LOG_DEBUG:
794 asprintf(string, "debug: %s", msg);
795 break;
796 case PM_LOG_ERROR:
797 asprintf(string, _("error: %s"), msg);
798 break;
799 case PM_LOG_WARNING:
800 asprintf(string, _("warning: %s"), msg);
801 break;
802 case PM_LOG_FUNCTION:
803 asprintf(string, _("function: %s"), msg);
804 break;
805 default:
806 break;
808 free(msg);
810 return(ret);
813 int pm_vfprintf(FILE *stream, pmloglevel_t level, const char *format, va_list args)
815 int ret = 0;
817 /* if current logmask does not overlap with level, do not print msg */
818 if(!(config->logmask & level)) {
819 return ret;
822 #if defined(PACMAN_DEBUG)
823 /* If debug is on, we'll timestamp the output */
824 if(config->logmask & PM_LOG_DEBUG) {
825 time_t t;
826 struct tm *tmp;
827 char timestr[10] = {0};
829 t = time(NULL);
830 tmp = localtime(&t);
831 strftime(timestr, 9, "%H:%M:%S", tmp);
832 timestr[8] = '\0';
834 printf("[%s] ", timestr);
836 #endif
838 /* print a prefix to the message */
839 switch(level) {
840 case PM_LOG_DEBUG:
841 fprintf(stream, "debug: ");
842 break;
843 case PM_LOG_ERROR:
844 fprintf(stream, _("error: "));
845 break;
846 case PM_LOG_WARNING:
847 fprintf(stream, _("warning: "));
848 break;
849 case PM_LOG_FUNCTION:
850 /* TODO we should increase the indent level when this occurs so we can see
851 * program flow easier. It'll be fun */
852 fprintf(stream, _("function: "));
853 break;
854 default:
855 break;
858 /* print the message using va_arg list */
859 ret = vfprintf(stream, format, args);
860 return(ret);
863 #ifndef HAVE_STRNDUP
864 /* A quick and dirty implementation derived from glibc */
865 static size_t strnlen(const char *s, size_t max)
867 register const char *p;
868 for(p = s; *p && max--; ++p);
869 return(p - s);
872 char *strndup(const char *s, size_t n)
874 size_t len = strnlen(s, n);
875 char *new = (char *) malloc(len + 1);
877 if (new == NULL)
878 return NULL;
880 new[len] = '\0';
881 return (char *) memcpy(new, s, len);
883 #endif
885 /* vim: set ts=2 sw=2 noet: */