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/>.
38 #include <sys/types.h>
41 #include <locale.h> /* setlocale */
45 #include <archive_entry.h>
48 #include <openssl/md5.h>
49 #include <openssl/sha.h>
59 #include "alpm_list.h"
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
)
77 if(strchr(delims
,**str
)!=NULL
) {
84 /* There is no other token */
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);
109 incr
= calloc(strlen(orig
) + 1, sizeof(char));
111 while((ptr
= strsep(&str
, "/"))) {
113 /* we have another path component- append the newest component to
114 * existing string and create one more level of dir structure */
117 if(access(incr
, F_OK
)) {
118 if(mkdir(incr
, mode
)) {
131 #define CPBUFSIZE 8 * 1024
133 int _alpm_copyfile(const char *src
, const char *dest
)
140 in
= fopen(src
, "rb");
144 out
= fopen(dest
, "wb");
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
))) {
155 nwritten
= fwrite(buf
, 1, len
, out
);
156 if((nwritten
!= len
) || ferror(out
)) {
162 /* chmod dest to permissions of src, as long as it is not a symlink */
164 if(!stat(src
, &statbuf
)) {
165 if(! S_ISLNK(statbuf
.st_mode
)) {
166 fchmod(fileno(out
), statbuf
.st_mode
);
169 /* stat was unsuccessful */
180 /* Trim whitespace and newlines from a string
182 char *_alpm_strtrim(char *str
)
187 /* string is empty, so we're done. */
191 while(isspace((unsigned char)*pch
)) {
195 memmove(str
, pch
, (strlen(pch
) + 1));
198 /* check if there wasn't anything but whitespace in the string. */
203 pch
= (str
+ (strlen(str
) - 1));
204 while(isspace((unsigned char)*pch
)) {
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
)
224 while(len
> 0 && str
[len
- 1] == '\n') {
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
;
248 if(filename
== NULL
) {
251 list
= alpm_list_add(list
, (void *)filename
);
252 ret
= _alpm_unpack(handle
, archive
, prefix
, list
, 1);
253 alpm_list_free(list
);
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
)
273 struct archive
*_archive
;
274 struct archive_entry
*entry
;
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"));
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
));
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. */
324 char *entry_prefix
= strdup(entryname
);
325 char *p
= strstr(entry_prefix
,"/");
329 char *found
= alpm_list_find_str(list
, entry_prefix
);
332 if(archive_read_data_skip(_archive
) != ARCHIVE_OK
) {
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
));
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
));
370 /* does the same thing as 'rm -rf' */
371 int _alpm_rmrf(const char *path
)
379 if(_alpm_lstat(path
, &st
) == 0) {
380 if(!S_ISDIR(st
.st_mode
)) {
384 if(errno
== ENOENT
) {
391 dirp
= opendir(path
);
395 for(dp
= readdir(dirp
); dp
!= NULL
; dp
= readdir(dirp
)) {
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
);
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
,
426 DIR *dir
= opendir(path
);
429 if(errno
== ENOTDIR
) {
430 _alpm_log(handle
, ALPM_LOG_DEBUG
, "%s was not a directory\n", path
);
432 _alpm_log(handle
, ALPM_LOG_DEBUG
, "could not read directory %s\n",
437 while((ent
= readdir(dir
)) != NULL
) {
438 const char *name
= ent
->d_name
;
440 if(strcmp(name
, ".") == 0 || strcmp(name
, "..") == 0) {
455 int _alpm_logaction(alpm_handle_t
*handle
, const char *fmt
, va_list args
)
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. */
463 va_copy(args_syslog
, args
);
464 vsyslog(LOG_WARNING
, fmt
, args_syslog
);
468 if(handle
->logstream
) {
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
);
486 int _alpm_run_chroot(alpm_handle_t
*handle
, const char *path
, char *const argv
[])
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"));
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
));
508 _alpm_log(handle
, ALPM_LOG_DEBUG
, "executing \"%s\" under chroot \"%s\"\n",
511 /* Flush open fds before fork() to avoid cloning buffers */
514 if(pipe(pipefd
) == -1) {
515 _alpm_log(handle
, ALPM_LOG_ERROR
, _("could not create pipe (%s)\n"), strerror(errno
));
520 /* fork- parent and child each have seperate code blocks below */
523 _alpm_log(handle
, ALPM_LOG_ERROR
, _("could not fork a new process (%s)\n"), strerror(errno
));
529 /* this code runs for the child only (the actual chroot/exec) */
532 while(dup2(pipefd
[1], 1) == -1 && errno
== EINTR
);
533 while(dup2(pipefd
[1], 2) == -1 && errno
== EINTR
);
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
));
542 if(chdir("/") != 0) {
543 fprintf(stderr
, _("could not change directory to %s (%s)\n"),
544 "/", strerror(errno
));
549 fprintf(stderr
, _("call to execv failed (%s)\n"), strerror(errno
));
552 /* this code runs for the parent only (wait on the child) */
557 pipe_file
= fdopen(pipefd
[0], "r");
558 if(pipe_file
== NULL
) {
562 while(!feof(pipe_file
)) {
564 if(fgets(line
, PATH_MAX
, pipe_file
) == NULL
)
566 alpm_logaction(handle
, "%s", line
);
567 EVENT(handle
->trans
, ALPM_TRANS_EVT_SCRIPTLET_INFO
, line
, NULL
);
572 while(waitpid(pid
, &status
, 0) == -1) {
574 _alpm_log(handle
, ALPM_LOG_ERROR
, _("call to waitpid failed (%s)\n"), strerror(errno
));
580 /* report error from above after the child has exited */
582 _alpm_log(handle
, ALPM_LOG_ERROR
, _("could not open pipe (%s)\n"), strerror(errno
));
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"));
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
));
603 int _alpm_ldconfig(alpm_handle_t
*handle
)
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
);
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
)
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
),
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
);
650 /* package wasn't found in any cachedir */
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
)
662 alpm_list_t
*i
, *tmp
;
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"),
672 if(_alpm_makepath(cachedir
) == 0) {
673 _alpm_log(handle
, ALPM_LOG_DEBUG
, "using cachedir: %s\n", 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
);
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
);
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"));
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
)
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
);
712 ret
= lstat(path
, buf
);
719 static int md5_file(const char *path
, unsigned char output
[16])
726 CALLOC(buf
, 8192, sizeof(unsigned char), return 1);
728 if((f
= fopen(path
, "rb")) == NULL
) {
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
));
753 /* third param is so we match the PolarSSL definition */
754 static int sha2_file(const char *path
, unsigned char output
[32], int is224
)
761 CALLOC(buf
, 8192, sizeof(unsigned char), return 1);
763 if((f
= fopen(path
, "rb")) == NULL
) {
774 while((n
= fread(buf
, 1, sizeof(buf
), f
)) > 0) {
776 SHA224_Update(&ctx
, buf
, n
);
778 SHA256_Update(&ctx
, buf
, n
);
783 SHA224_Final(output
, &ctx
);
785 SHA256_Final(output
, &ctx
);
788 memset(&ctx
, 0, sizeof(SHA256_CTX
));
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];
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
);
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
]);
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];
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);
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
]);
863 int _alpm_test_checksum(const char *filepath
, const char *expected
,
864 enum _alpm_csum type
)
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
);
877 if(expected
== NULL
|| computed
== NULL
) {
879 } else if(strcmp(expected
, computed
) != 0) {
889 /* Note: does NOT handle sparse files on purpose for speed. */
890 int _alpm_archive_fgets(struct archive
*a
, struct archive_read_buffer
*b
)
896 /* ensure we start populating our line buffer at the beginning */
897 b
->line_offset
= b
->line
;
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 */
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
;
913 if(b
->ret
< ARCHIVE_OK
) {
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') {
927 /* allocate our buffer, or ensure our existing one is big enough */
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
;
934 size_t needed
= (size_t)((b
->line_offset
- b
->line
)
935 + (i
- b
->block_offset
) + 1);
936 if(needed
> b
->max_line_size
) {
940 if(needed
> b
->line_size
) {
941 /* need to realloc + copy data to fit total length */
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
);
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 */
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
;
965 /* there was no new data, return what is left; saved ARCHIVE_EOF will be
966 * returned on next call */
968 b
->line_offset
[0] = '\0';
978 memset(b
, 0, sizeof(b
));
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
;
998 /* remove anything trailing a '/' */
999 end
= strchr(target
, '/');
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
) {
1012 /* copy into fields and return */
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);
1026 STRNDUP(*name
, target
, pkgver
- target
, return -1);
1028 *name_hash
= _alpm_hash_sdbm(*name
);
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;
1049 while((c
= *str
++)) {
1050 hash
= c
+ (hash
<< 6) + (hash
<< 16) - 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
);
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
)
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
);
1096 ret
= access(file
, amode
);
1101 _alpm_log(handle
, ALPM_LOG_DEBUG
, "\"%s%s\" is not readable: %s\n",
1102 dir
, file
, strerror(errno
));
1105 _alpm_log(handle
, ALPM_LOG_DEBUG
, "\"%s%s\" is not writable: %s\n",
1106 dir
, file
, strerror(errno
));
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
));
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
);
1129 char *strndup(const char *s
, size_t n
)
1131 size_t len
= strnlen(s
, n
);
1132 char *new = (char *) malloc(len
+ 1);
1138 return (char *)memcpy(new, s
, len
);
1142 /* vim: set ts=2 sw=2 noet: */