Remove usages of alpm_list_next() in backend
[pacman-ng.git] / lib / libalpm / util.c
blob27709c644d1bb76cc21de7b48309cf6ce757ed47
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>
6 * Copyright (c) 2005 by Aurelien Foret <orelien@chez.com>
7 * Copyright (c) 2005 by Christian Hamar <krics@linuxforum.hu>
8 * Copyright (c) 2006 by David Kimpe <dnaku@frugalware.org>
9 * Copyright (c) 2005, 2006 by Miklos Vajna <vmiklos@frugalware.org>
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program. If not, see <http://www.gnu.org/licenses/>.
25 #include "config.h"
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <stdarg.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <ctype.h>
33 #include <dirent.h>
34 #include <time.h>
35 #include <syslog.h>
36 #include <errno.h>
37 #include <limits.h>
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <sys/wait.h>
41 #include <locale.h> /* setlocale */
43 /* libarchive */
44 #include <archive.h>
45 #include <archive_entry.h>
47 #ifdef HAVE_LIBSSL
48 #include <openssl/md5.h>
49 #include <openssl/sha.h>
50 #else
51 #include "md5.h"
52 #include "sha2.h"
53 #endif
55 /* libalpm */
56 #include "util.h"
57 #include "log.h"
58 #include "alpm.h"
59 #include "alpm_list.h"
60 #include "handle.h"
61 #include "trans.h"
63 #ifndef HAVE_STRSEP
64 /* This is a replacement for strsep which is not portable (missing on Solaris).
65 * Copyright (c) 2001 by François Gouget <fgouget_at_codeweavers.com> */
66 char* strsep(char** str, const char* delims)
68 char* token;
70 if(*str==NULL) {
71 /* No more tokens */
72 return NULL;
75 token=*str;
76 while(**str!='\0') {
77 if(strchr(delims,**str)!=NULL) {
78 **str='\0';
79 (*str)++;
80 return token;
82 (*str)++;
84 /* There is no other token */
85 *str=NULL;
86 return token;
88 #endif
90 int _alpm_makepath(const char *path)
92 return _alpm_makepath_mode(path, 0755);
95 /* does the same thing as 'mkdir -p' */
96 int _alpm_makepath_mode(const char *path, mode_t mode)
98 /* A bit of pointer hell here. Descriptions:
99 * orig - a copy of path so we can safely butcher it with strsep
100 * str - the current position in the path string (after the delimiter)
101 * ptr - the original position of str after calling strsep
102 * incr - incrementally generated path for use in stat/mkdir call
104 char *orig, *str, *ptr, *incr;
105 mode_t oldmask = umask(0000);
106 int ret = 0;
108 orig = strdup(path);
109 incr = calloc(strlen(orig) + 1, sizeof(char));
110 str = orig;
111 while((ptr = strsep(&str, "/"))) {
112 if(strlen(ptr)) {
113 /* we have another path component- append the newest component to
114 * existing string and create one more level of dir structure */
115 strcat(incr, "/");
116 strcat(incr, ptr);
117 if(access(incr, F_OK)) {
118 if(mkdir(incr, mode)) {
119 ret = 1;
120 break;
125 free(orig);
126 free(incr);
127 umask(oldmask);
128 return ret;
131 #define CPBUFSIZE 8 * 1024
133 int _alpm_copyfile(const char *src, const char *dest)
135 FILE *in, *out;
136 size_t len;
137 char *buf;
138 int ret = 0;
140 in = fopen(src, "rb");
141 if(in == NULL) {
142 return 1;
144 out = fopen(dest, "wb");
145 if(out == NULL) {
146 fclose(in);
147 return 1;
150 CALLOC(buf, (size_t)CPBUFSIZE, (size_t)1, ret = 1; goto cleanup;);
152 /* do the actual file copy */
153 while((len = fread(buf, 1, CPBUFSIZE, in))) {
154 size_t nwritten = 0;
155 nwritten = fwrite(buf, 1, len, out);
156 if((nwritten != len) || ferror(out)) {
157 ret = -1;
158 goto cleanup;
162 /* chmod dest to permissions of src, as long as it is not a symlink */
163 struct stat statbuf;
164 if(!stat(src, &statbuf)) {
165 if(! S_ISLNK(statbuf.st_mode)) {
166 fchmod(fileno(out), statbuf.st_mode);
168 } else {
169 /* stat was unsuccessful */
170 ret = 1;
173 cleanup:
174 fclose(in);
175 fclose(out);
176 FREE(buf);
177 return ret;
180 /* Trim whitespace and newlines from a string
182 char *_alpm_strtrim(char *str)
184 char *pch = str;
186 if(*str == '\0') {
187 /* string is empty, so we're done. */
188 return str;
191 while(isspace((unsigned char)*pch)) {
192 pch++;
194 if(pch != str) {
195 memmove(str, pch, (strlen(pch) + 1));
198 /* check if there wasn't anything but whitespace in the string. */
199 if(*str == '\0') {
200 return str;
203 pch = (str + (strlen(str) - 1));
204 while(isspace((unsigned char)*pch)) {
205 pch--;
207 *++pch = '\0';
209 return str;
213 * Trim trailing newline from a string (if one exists).
214 * @param str a single line of text
215 * @return the length of the trimmed string
217 size_t _alpm_strip_newline(char *str)
219 size_t len;
220 if(str == '\0') {
221 return 0;
223 len = strlen(str);
224 while(len > 0 && str[len - 1] == '\n') {
225 len--;
227 str[len] = '\0';
229 return len;
232 /* Compression functions */
235 * @brief Unpack a specific file in an archive.
237 * @param handle the context handle
238 * @param archive the archive to unpack
239 * @param prefix where to extract the files
240 * @param filename a file within the archive to unpack
241 * @return 0 on success, 1 on failure
243 int _alpm_unpack_single(alpm_handle_t *handle, const char *archive,
244 const char *prefix, const char *filename)
246 alpm_list_t *list = NULL;
247 int ret = 0;
248 if(filename == NULL) {
249 return 1;
251 list = alpm_list_add(list, (void *)filename);
252 ret = _alpm_unpack(handle, archive, prefix, list, 1);
253 alpm_list_free(list);
254 return ret;
258 * @brief Unpack a list of files in an archive.
260 * @param handle the context handle
261 * @param archive the archive to unpack
262 * @param prefix where to extract the files
263 * @param list a list of files within the archive to unpack or NULL for all
264 * @param breakfirst break after the first entry found
266 * @return 0 on success, 1 on failure
268 int _alpm_unpack(alpm_handle_t *handle, const char *archive, const char *prefix,
269 alpm_list_t *list, int breakfirst)
271 int ret = 0;
272 mode_t oldmask;
273 struct archive *_archive;
274 struct archive_entry *entry;
275 char cwd[PATH_MAX];
276 int restore_cwd = 0;
278 if((_archive = archive_read_new()) == NULL) {
279 RET_ERR(handle, ALPM_ERR_LIBARCHIVE, 1);
282 archive_read_support_compression_all(_archive);
283 archive_read_support_format_all(_archive);
285 if(archive_read_open_filename(_archive, archive,
286 ARCHIVE_DEFAULT_BYTES_PER_BLOCK) != ARCHIVE_OK) {
287 _alpm_log(handle, ALPM_LOG_ERROR, _("could not open file %s: %s\n"), archive,
288 archive_error_string(_archive));
289 RET_ERR(handle, ALPM_ERR_PKG_OPEN, 1);
292 oldmask = umask(0022);
294 /* save the cwd so we can restore it later */
295 if(getcwd(cwd, PATH_MAX) == NULL) {
296 _alpm_log(handle, ALPM_LOG_ERROR, _("could not get current working directory\n"));
297 } else {
298 restore_cwd = 1;
301 /* just in case our cwd was removed in the upgrade operation */
302 if(chdir(prefix) != 0) {
303 _alpm_log(handle, ALPM_LOG_ERROR, _("could not change directory to %s (%s)\n"),
304 prefix, strerror(errno));
305 ret = 1;
306 goto cleanup;
309 while(archive_read_next_header(_archive, &entry) == ARCHIVE_OK) {
310 const struct stat *st;
311 const char *entryname; /* the name of the file in the archive */
313 st = archive_entry_stat(entry);
314 entryname = archive_entry_pathname(entry);
316 if(S_ISREG(st->st_mode)) {
317 archive_entry_set_perm(entry, 0644);
318 } else if(S_ISDIR(st->st_mode)) {
319 archive_entry_set_perm(entry, 0755);
322 /* If specific files were requested, skip entries that don't match. */
323 if(list) {
324 char *entry_prefix = strdup(entryname);
325 char *p = strstr(entry_prefix,"/");
326 if(p) {
327 *(p+1) = '\0';
329 char *found = alpm_list_find_str(list, entry_prefix);
330 free(entry_prefix);
331 if(!found) {
332 if(archive_read_data_skip(_archive) != ARCHIVE_OK) {
333 ret = 1;
334 goto cleanup;
336 continue;
337 } else {
338 _alpm_log(handle, ALPM_LOG_DEBUG, "extracting: %s\n", entryname);
342 /* Extract the archive entry. */
343 int readret = archive_read_extract(_archive, entry, 0);
344 if(readret == ARCHIVE_WARN) {
345 /* operation succeeded but a non-critical error was encountered */
346 _alpm_log(handle, ALPM_LOG_WARNING, _("warning given when extracting %s (%s)\n"),
347 entryname, archive_error_string(_archive));
348 } else if(readret != ARCHIVE_OK) {
349 _alpm_log(handle, ALPM_LOG_ERROR, _("could not extract %s (%s)\n"),
350 entryname, archive_error_string(_archive));
351 ret = 1;
352 goto cleanup;
355 if(breakfirst) {
356 break;
360 cleanup:
361 umask(oldmask);
362 archive_read_finish(_archive);
363 if(restore_cwd && chdir(cwd) != 0) {
364 _alpm_log(handle, ALPM_LOG_ERROR, _("could not change directory to %s (%s)\n"),
365 cwd, strerror(errno));
367 return ret;
370 /* does the same thing as 'rm -rf' */
371 int _alpm_rmrf(const char *path)
373 int errflag = 0;
374 struct dirent *dp;
375 DIR *dirp;
376 char name[PATH_MAX];
377 struct stat st;
379 if(_alpm_lstat(path, &st) == 0) {
380 if(!S_ISDIR(st.st_mode)) {
381 if(!unlink(path)) {
382 return 0;
383 } else {
384 if(errno == ENOENT) {
385 return 0;
386 } else {
387 return 1;
390 } else {
391 dirp = opendir(path);
392 if(!dirp) {
393 return 1;
395 for(dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
396 if(dp->d_ino) {
397 sprintf(name, "%s/%s", path, dp->d_name);
398 if(strcmp(dp->d_name, "..") != 0 && strcmp(dp->d_name, ".") != 0) {
399 errflag += _alpm_rmrf(name);
403 closedir(dirp);
404 if(rmdir(path)) {
405 errflag++;
408 return errflag;
410 return 0;
414 * Determine if there are files in a directory
415 * @param handle the context handle
416 * @param path the full absolute directory path
417 * @param full_count whether to return an exact count of files
418 * @return a file count if full_count is != 0, else >0 if directory has
419 * contents, 0 if no contents, and -1 on error
421 ssize_t _alpm_files_in_directory(alpm_handle_t *handle, const char *path,
422 int full_count)
424 ssize_t files = 0;
425 struct dirent *ent;
426 DIR *dir = opendir(path);
428 if(!dir) {
429 if(errno == ENOTDIR) {
430 _alpm_log(handle, ALPM_LOG_DEBUG, "%s was not a directory\n", path);
431 } else {
432 _alpm_log(handle, ALPM_LOG_DEBUG, "could not read directory %s\n",
433 path);
435 return -1;
437 while((ent = readdir(dir)) != NULL) {
438 const char *name = ent->d_name;
440 if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
441 continue;
444 files++;
446 if(!full_count) {
447 break;
451 closedir(dir);
452 return files;
455 int _alpm_logaction(alpm_handle_t *handle, const char *fmt, va_list args)
457 int ret = 0;
459 if(handle->usesyslog) {
460 /* we can't use a va_list more than once, so we need to copy it
461 * so we can use the original when calling vfprintf below. */
462 va_list args_syslog;
463 va_copy(args_syslog, args);
464 vsyslog(LOG_WARNING, fmt, args_syslog);
465 va_end(args_syslog);
468 if(handle->logstream) {
469 time_t t;
470 struct tm *tm;
472 t = time(NULL);
473 tm = localtime(&t);
475 /* Use ISO-8601 date format */
476 fprintf(handle->logstream, "[%04d-%02d-%02d %02d:%02d] ",
477 tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
478 tm->tm_hour, tm->tm_min);
479 ret = vfprintf(handle->logstream, fmt, args);
480 fflush(handle->logstream);
483 return ret;
486 int _alpm_run_chroot(alpm_handle_t *handle, const char *path, char *const argv[])
488 char cwd[PATH_MAX];
489 pid_t pid;
490 int pipefd[2];
491 int restore_cwd = 0;
492 int retval = 0;
494 /* save the cwd so we can restore it later */
495 if(getcwd(cwd, PATH_MAX) == NULL) {
496 _alpm_log(handle, ALPM_LOG_ERROR, _("could not get current working directory\n"));
497 } else {
498 restore_cwd = 1;
501 /* just in case our cwd was removed in the upgrade operation */
502 if(chdir(handle->root) != 0) {
503 _alpm_log(handle, ALPM_LOG_ERROR, _("could not change directory to %s (%s)\n"),
504 handle->root, strerror(errno));
505 goto cleanup;
508 _alpm_log(handle, ALPM_LOG_DEBUG, "executing \"%s\" under chroot \"%s\"\n",
509 path, handle->root);
511 /* Flush open fds before fork() to avoid cloning buffers */
512 fflush(NULL);
514 if(pipe(pipefd) == -1) {
515 _alpm_log(handle, ALPM_LOG_ERROR, _("could not create pipe (%s)\n"), strerror(errno));
516 retval = 1;
517 goto cleanup;
520 /* fork- parent and child each have seperate code blocks below */
521 pid = fork();
522 if(pid == -1) {
523 _alpm_log(handle, ALPM_LOG_ERROR, _("could not fork a new process (%s)\n"), strerror(errno));
524 retval = 1;
525 goto cleanup;
528 if(pid == 0) {
529 /* this code runs for the child only (the actual chroot/exec) */
530 close(1);
531 close(2);
532 while(dup2(pipefd[1], 1) == -1 && errno == EINTR);
533 while(dup2(pipefd[1], 2) == -1 && errno == EINTR);
534 close(pipefd[0]);
535 close(pipefd[1]);
537 /* use fprintf instead of _alpm_log to send output through the parent */
538 if(chroot(handle->root) != 0) {
539 fprintf(stderr, _("could not change the root directory (%s)\n"), strerror(errno));
540 exit(1);
542 if(chdir("/") != 0) {
543 fprintf(stderr, _("could not change directory to %s (%s)\n"),
544 "/", strerror(errno));
545 exit(1);
547 umask(0022);
548 execv(path, argv);
549 fprintf(stderr, _("call to execv failed (%s)\n"), strerror(errno));
550 exit(1);
551 } else {
552 /* this code runs for the parent only (wait on the child) */
553 int status;
554 FILE *pipe_file;
556 close(pipefd[1]);
557 pipe_file = fdopen(pipefd[0], "r");
558 if(pipe_file == NULL) {
559 close(pipefd[0]);
560 retval = 1;
561 } else {
562 while(!feof(pipe_file)) {
563 char line[PATH_MAX];
564 if(fgets(line, PATH_MAX, pipe_file) == NULL)
565 break;
566 alpm_logaction(handle, "%s", line);
567 EVENT(handle->trans, ALPM_TRANS_EVT_SCRIPTLET_INFO, line, NULL);
569 fclose(pipe_file);
572 while(waitpid(pid, &status, 0) == -1) {
573 if(errno != EINTR) {
574 _alpm_log(handle, ALPM_LOG_ERROR, _("call to waitpid failed (%s)\n"), strerror(errno));
575 retval = 1;
576 goto cleanup;
580 /* report error from above after the child has exited */
581 if(retval != 0) {
582 _alpm_log(handle, ALPM_LOG_ERROR, _("could not open pipe (%s)\n"), strerror(errno));
583 goto cleanup;
585 /* check the return status, make sure it is 0 (success) */
586 if(WIFEXITED(status)) {
587 _alpm_log(handle, ALPM_LOG_DEBUG, "call to waitpid succeeded\n");
588 if(WEXITSTATUS(status) != 0) {
589 _alpm_log(handle, ALPM_LOG_ERROR, _("command failed to execute correctly\n"));
590 retval = 1;
595 cleanup:
596 if(restore_cwd && chdir(cwd) != 0) {
597 _alpm_log(handle, ALPM_LOG_ERROR, _("could not change directory to %s (%s)\n"), cwd, strerror(errno));
600 return retval;
603 int _alpm_ldconfig(alpm_handle_t *handle)
605 char line[PATH_MAX];
607 _alpm_log(handle, ALPM_LOG_DEBUG, "running ldconfig\n");
609 snprintf(line, PATH_MAX, "%setc/ld.so.conf", handle->root);
610 if(access(line, F_OK) == 0) {
611 snprintf(line, PATH_MAX, "%ssbin/ldconfig", handle->root);
612 if(access(line, X_OK) == 0) {
613 char *argv[] = { "ldconfig", NULL };
614 _alpm_run_chroot(handle, "/sbin/ldconfig", argv);
618 return 0;
621 /* Helper function for comparing strings using the
622 * alpm "compare func" signature */
623 int _alpm_str_cmp(const void *s1, const void *s2)
625 return strcmp(s1, s2);
628 /** Find a filename in a registered alpm cachedir.
629 * @param handle the context handle
630 * @param filename name of file to find
631 * @return malloced path of file, NULL if not found
633 char *_alpm_filecache_find(alpm_handle_t *handle, const char *filename)
635 char path[PATH_MAX];
636 char *retpath;
637 alpm_list_t *i;
638 struct stat buf;
640 /* Loop through the cache dirs until we find a matching file */
641 for(i = alpm_option_get_cachedirs(handle); i; i = i->next) {
642 snprintf(path, PATH_MAX, "%s%s", (char *)alpm_list_getdata(i),
643 filename);
644 if(stat(path, &buf) == 0 && S_ISREG(buf.st_mode)) {
645 retpath = strdup(path);
646 _alpm_log(handle, ALPM_LOG_DEBUG, "found cached pkg: %s\n", retpath);
647 return retpath;
650 /* package wasn't found in any cachedir */
651 return NULL;
654 /** Check the alpm cachedirs for existance and find a writable one.
655 * If no valid cache directory can be found, use /tmp.
656 * @param handle the context handle
657 * @return pointer to a writable cache directory.
659 const char *_alpm_filecache_setup(alpm_handle_t *handle)
661 struct stat buf;
662 alpm_list_t *i, *tmp;
663 char *cachedir;
665 /* Loop through the cache dirs until we find a writeable dir */
666 for(i = alpm_option_get_cachedirs(handle); i; i = i->next) {
667 cachedir = alpm_list_getdata(i);
668 if(stat(cachedir, &buf) != 0) {
669 /* cache directory does not exist.... try creating it */
670 _alpm_log(handle, ALPM_LOG_WARNING, _("no %s cache exists, creating...\n"),
671 cachedir);
672 if(_alpm_makepath(cachedir) == 0) {
673 _alpm_log(handle, ALPM_LOG_DEBUG, "using cachedir: %s\n", cachedir);
674 return cachedir;
676 } else if(S_ISDIR(buf.st_mode) && (buf.st_mode & S_IWUSR)) {
677 _alpm_log(handle, ALPM_LOG_DEBUG, "using cachedir: %s\n", cachedir);
678 return cachedir;
679 } else {
680 _alpm_log(handle, ALPM_LOG_DEBUG, "skipping cachedir: %s\n", cachedir);
684 /* we didn't find a valid cache directory. use /tmp. */
685 tmp = alpm_list_add(NULL, "/tmp/");
686 alpm_option_set_cachedirs(handle, tmp);
687 alpm_list_free(tmp);
688 _alpm_log(handle, ALPM_LOG_DEBUG, "using cachedir: %s\n", "/tmp/");
689 _alpm_log(handle, ALPM_LOG_WARNING, _("couldn't create package cache, using /tmp instead\n"));
690 return "/tmp/";
693 /** lstat wrapper that treats /path/dirsymlink/ the same as /path/dirsymlink.
694 * Linux lstat follows POSIX semantics and still performs a dereference on
695 * the first, and for uses of lstat in libalpm this is not what we want.
696 * @param path path to file to lstat
697 * @param buf structure to fill with stat information
698 * @return the return code from lstat
700 int _alpm_lstat(const char *path, struct stat *buf)
702 int ret;
703 size_t len = strlen(path);
705 /* strip the trailing slash if one exists */
706 if(len != 0 && path[len - 1] == '/') {
707 char *newpath = strdup(path);
708 newpath[len - 1] = '\0';
709 ret = lstat(newpath, buf);
710 free(newpath);
711 } else {
712 ret = lstat(path, buf);
715 return ret;
718 #ifdef HAVE_LIBSSL
719 static int md5_file(const char *path, unsigned char output[16])
721 FILE *f;
722 size_t n;
723 MD5_CTX ctx;
724 unsigned char *buf;
726 CALLOC(buf, 8192, sizeof(unsigned char), return 1);
728 if((f = fopen(path, "rb")) == NULL) {
729 free(buf);
730 return 1;
733 MD5_Init(&ctx);
735 while((n = fread(buf, 1, sizeof(buf), f)) > 0) {
736 MD5_Update(&ctx, buf, n);
739 MD5_Final(output, &ctx);
741 memset(&ctx, 0, sizeof(MD5_CTX));
742 free(buf);
744 if(ferror(f) != 0) {
745 fclose(f);
746 return 2;
749 fclose(f);
750 return 0;
753 /* third param is so we match the PolarSSL definition */
754 static int sha2_file(const char *path, unsigned char output[32], int is224)
756 FILE *f;
757 size_t n;
758 SHA256_CTX ctx;
759 unsigned char *buf;
761 CALLOC(buf, 8192, sizeof(unsigned char), return 1);
763 if((f = fopen(path, "rb")) == NULL) {
764 free(buf);
765 return 1;
768 if(is224) {
769 SHA224_Init(&ctx);
770 } else {
771 SHA256_Init(&ctx);
774 while((n = fread(buf, 1, sizeof(buf), f)) > 0) {
775 if(is224) {
776 SHA224_Update(&ctx, buf, n);
777 } else {
778 SHA256_Update(&ctx, buf, n);
782 if(is224) {
783 SHA224_Final(output, &ctx);
784 } else {
785 SHA256_Final(output, &ctx);
788 memset(&ctx, 0, sizeof(SHA256_CTX));
789 free(buf);
791 if(ferror(f) != 0) {
792 fclose(f);
793 return 2;
796 fclose(f);
797 return 0;
799 #endif
801 /** Get the md5 sum of file.
802 * @param filename name of the file
803 * @return the checksum on success, NULL on error
804 * @addtogroup alpm_misc
806 char SYMEXPORT *alpm_compute_md5sum(const char *filename)
808 unsigned char output[16];
809 char *md5sum;
810 int ret, i;
812 ASSERT(filename != NULL, return NULL);
814 /* allocate 32 chars plus 1 for null */
815 CALLOC(md5sum, 33, sizeof(char), return NULL);
816 /* defined above for OpenSSL, otherwise defined in md5.h */
817 ret = md5_file(filename, output);
819 if(ret > 0) {
820 return NULL;
823 /* Convert the result to something readable */
824 for (i = 0; i < 16; i++) {
825 /* sprintf is acceptable here because we know our output */
826 sprintf(md5sum +(i * 2), "%02x", output[i]);
829 return md5sum;
832 /** Get the sha256 sum of file.
833 * @param filename name of the file
834 * @return the checksum on success, NULL on error
835 * @addtogroup alpm_misc
837 char SYMEXPORT *alpm_compute_sha256sum(const char *filename)
839 unsigned char output[32];
840 char *sha256sum;
841 int ret, i;
843 ASSERT(filename != NULL, return NULL);
845 /* allocate 64 chars plus 1 for null */
846 CALLOC(sha256sum, 65, sizeof(char), return NULL);
847 /* defined above for OpenSSL, otherwise defined in sha2.h */
848 ret = sha2_file(filename, output, 0);
850 if(ret > 0) {
851 return NULL;
854 /* Convert the result to something readable */
855 for (i = 0; i < 32; i++) {
856 /* sprintf is acceptable here because we know our output */
857 sprintf(sha256sum +(i * 2), "%02x", output[i]);
860 return sha256sum;
863 int _alpm_test_checksum(const char *filepath, const char *expected,
864 enum _alpm_csum type)
866 char *computed;
867 int ret;
869 if(type == ALPM_CSUM_MD5) {
870 computed = alpm_compute_md5sum(filepath);
871 } else if(type == ALPM_CSUM_SHA256) {
872 computed = alpm_compute_sha256sum(filepath);
873 } else {
874 return -1;
877 if(expected == NULL || computed == NULL) {
878 ret = -1;
879 } else if(strcmp(expected, computed) != 0) {
880 ret = 1;
881 } else {
882 ret = 0;
885 FREE(computed);
886 return ret;
889 /* Note: does NOT handle sparse files on purpose for speed. */
890 int _alpm_archive_fgets(struct archive *a, struct archive_read_buffer *b)
892 char *i = NULL;
893 int64_t offset;
894 int done = 0;
896 /* ensure we start populating our line buffer at the beginning */
897 b->line_offset = b->line;
899 while(1) {
900 /* have we processed this entire block? */
901 if(b->block + b->block_size == b->block_offset) {
902 if(b->ret == ARCHIVE_EOF) {
903 /* reached end of archive on the last read, now we are out of data */
904 goto cleanup;
907 /* zero-copy - this is the entire next block of data. */
908 b->ret = archive_read_data_block(a, (void *)&b->block,
909 &b->block_size, &offset);
910 b->block_offset = b->block;
912 /* error, cleanup */
913 if(b->ret < ARCHIVE_OK) {
914 goto cleanup;
918 /* loop through the block looking for EOL characters */
919 for(i = b->block_offset; i < (b->block + b->block_size); i++) {
920 /* check if read value was null or newline */
921 if(*i == '\0' || *i == '\n') {
922 done = 1;
923 break;
927 /* allocate our buffer, or ensure our existing one is big enough */
928 if(!b->line) {
929 /* set the initial buffer to the read block_size */
930 CALLOC(b->line, b->block_size + 1, sizeof(char), b->ret = -ENOMEM; goto cleanup);
931 b->line_size = b->block_size + 1;
932 b->line_offset = b->line;
933 } else {
934 size_t needed = (size_t)((b->line_offset - b->line)
935 + (i - b->block_offset) + 1);
936 if(needed > b->max_line_size) {
937 b->ret = -ERANGE;
938 goto cleanup;
940 if(needed > b->line_size) {
941 /* need to realloc + copy data to fit total length */
942 char *new;
943 CALLOC(new, needed, sizeof(char), b->ret = -ENOMEM; goto cleanup);
944 memcpy(new, b->line, b->line_size);
945 b->line_size = needed;
946 b->line_offset = new + (b->line_offset - b->line);
947 free(b->line);
948 b->line = new;
952 if(done) {
953 size_t len = (size_t)(i - b->block_offset);
954 memcpy(b->line_offset, b->block_offset, len);
955 b->line_offset[len] = '\0';
956 b->block_offset = ++i;
957 /* this is the main return point; from here you can read b->line */
958 return ARCHIVE_OK;
959 } else {
960 /* we've looked through the whole block but no newline, copy it */
961 size_t len = (size_t)(b->block + b->block_size - b->block_offset);
962 memcpy(b->line_offset, b->block_offset, len);
963 b->line_offset += len;
964 b->block_offset = i;
965 /* there was no new data, return what is left; saved ARCHIVE_EOF will be
966 * returned on next call */
967 if(len == 0) {
968 b->line_offset[0] = '\0';
969 return ARCHIVE_OK;
974 cleanup:
976 int ret = b->ret;
977 FREE(b->line);
978 memset(b, 0, sizeof(b));
979 return ret;
983 int _alpm_splitname(const char *target, char **name, char **version,
984 unsigned long *name_hash)
986 /* the format of a db entry is as follows:
987 * package-version-rel/
988 * package-version-rel/desc (we ignore the filename portion)
989 * package name can contain hyphens, so parse from the back- go back
990 * two hyphens and we have split the version from the name.
992 const char *pkgver, *end;
994 if(target == NULL) {
995 return -1;
998 /* remove anything trailing a '/' */
999 end = strchr(target, '/');
1000 if(!end) {
1001 end = target + strlen(target);
1004 /* do the magic parsing- find the beginning of the version string
1005 * by doing two iterations of same loop to lop off two hyphens */
1006 for(pkgver = end - 1; *pkgver && *pkgver != '-'; pkgver--);
1007 for(pkgver = pkgver - 1; *pkgver && *pkgver != '-'; pkgver--);
1008 if(*pkgver != '-' || pkgver == target) {
1009 return -1;
1012 /* copy into fields and return */
1013 if(version) {
1014 if(*version) {
1015 FREE(*version);
1017 /* version actually points to the dash, so need to increment 1 and account
1018 * for potential end character */
1019 STRNDUP(*version, pkgver + 1, end - pkgver - 1, return -1);
1022 if(name) {
1023 if(*name) {
1024 FREE(*name);
1026 STRNDUP(*name, target, pkgver - target, return -1);
1027 if(name_hash) {
1028 *name_hash = _alpm_hash_sdbm(*name);
1032 return 0;
1036 * Hash the given string to an unsigned long value.
1037 * This is the standard sdbm hashing algorithm.
1038 * @param str string to hash
1039 * @return the hash value of the given string
1041 unsigned long _alpm_hash_sdbm(const char *str)
1043 unsigned long hash = 0;
1044 int c;
1046 if(!str) {
1047 return hash;
1049 while((c = *str++)) {
1050 hash = c + (hash << 6) + (hash << 16) - hash;
1053 return hash;
1056 long _alpm_parsedate(const char *line)
1058 if(isalpha((unsigned char)line[0])) {
1059 /* initialize to null in case of failure */
1060 struct tm tmp_tm = { 0 };
1061 setlocale(LC_TIME, "C");
1062 strptime(line, "%a %b %e %H:%M:%S %Y", &tmp_tm);
1063 setlocale(LC_TIME, "");
1064 return mktime(&tmp_tm);
1066 return atol(line);
1070 * Wrapper around access() which takes a dir and file argument
1071 * separately and generates an appropriate error message.
1072 * If dir is NULL file will be treated as the whole path.
1073 * @param handle an alpm handle
1074 * @param dir directory path ending with and slash
1075 * @param file filename
1076 * @param amode access mode as described in access()
1077 * @return int value returned by access()
1080 int _alpm_access(alpm_handle_t *handle, const char *dir, const char *file, int amode)
1082 size_t len = 0;
1083 int ret = 0;
1085 if (dir) {
1086 char *check_path;
1088 len = strlen(dir) + strlen(file) + 1;
1089 CALLOC(check_path, len, sizeof(char), RET_ERR(handle, ALPM_ERR_MEMORY, -1));
1090 snprintf(check_path, len, "%s%s", dir, file);
1092 ret = access(check_path, amode);
1093 free(check_path);
1094 } else {
1095 dir = "";
1096 ret = access(file, amode);
1099 if(ret != 0) {
1100 if (amode & R_OK) {
1101 _alpm_log(handle, ALPM_LOG_DEBUG, "\"%s%s\" is not readable: %s\n",
1102 dir, file, strerror(errno));
1104 if (amode & W_OK) {
1105 _alpm_log(handle, ALPM_LOG_DEBUG, "\"%s%s\" is not writable: %s\n",
1106 dir, file, strerror(errno));
1108 if (amode & X_OK) {
1109 _alpm_log(handle, ALPM_LOG_DEBUG, "\"%s%s\" is not executable: %s\n",
1110 dir, file, strerror(errno));
1112 if (amode == F_OK) {
1113 _alpm_log(handle, ALPM_LOG_DEBUG, "\"%s%s\" does not exist: %s\n",
1114 dir, file, strerror(errno));
1117 return ret;
1120 #ifndef HAVE_STRNDUP
1121 /* A quick and dirty implementation derived from glibc */
1122 static size_t strnlen(const char *s, size_t max)
1124 register const char *p;
1125 for(p = s; *p && max--; ++p);
1126 return (p - s);
1129 char *strndup(const char *s, size_t n)
1131 size_t len = strnlen(s, n);
1132 char *new = (char *) malloc(len + 1);
1134 if(new == NULL)
1135 return NULL;
1137 new[len] = '\0';
1138 return (char *)memcpy(new, s, len);
1140 #endif
1142 /* vim: set ts=2 sw=2 noet: */