4 * Copyright (c) 2006-2013 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/>.
34 #include <locale.h> /* setlocale */
39 #include <archive_entry.h>
42 #include <openssl/md5.h>
43 #include <openssl/sha.h>
52 #include "libarchive-compat.h"
54 #include "alpm_list.h"
59 /** Extracts tokens from a string.
60 * Replaces strset which is not portable (missing on Solaris).
61 * Copyright (c) 2001 by François Gouget <fgouget_at_codeweavers.com>
62 * Modifies str to point to the first character after the token if one is
63 * found, or NULL if one is not.
64 * @param str string containing delimited tokens to parse
65 * @param delim character delimiting tokens in str
66 * @return pointer to the first token in str if str is not NULL, NULL if
69 char *strsep(char **str
, const char *delims
)
79 while(**str
!= '\0') {
80 if(strchr(delims
, **str
) != NULL
) {
87 /* There is no other token */
93 int _alpm_makepath(const char *path
)
95 return _alpm_makepath_mode(path
, 0755);
98 /** Creates a directory, including parents if needed, similar to 'mkdir -p'.
99 * @param path directory path to create
100 * @param mode permission mode for created directories
101 * @return 0 on success, 1 on error
103 int _alpm_makepath_mode(const char *path
, mode_t mode
)
109 STRDUP(str
, path
, return 1);
111 oldmask
= umask(0000);
113 for(ptr
= str
; *ptr
; ptr
++) {
114 /* detect mid-path condition and zero length paths */
115 if(*ptr
!= '/' || ptr
== str
|| ptr
[-1] == '/') {
119 /* temporarily mask the end of the path */
122 if(mkdir(str
, mode
) < 0 && errno
!= EEXIST
) {
127 /* restore path separator */
131 /* end of the string. add the full path. It will already exist when the path
132 * passed in has a trailing slash. */
133 if(mkdir(str
, mode
) < 0 && errno
!= EEXIST
) {
144 * @param src file path to copy from
145 * @param dest file path to copy to
146 * @return 0 on success, 1 on error
148 int _alpm_copyfile(const char *src
, const char *dest
)
151 int in
, out
, ret
= 1;
155 MALLOC(buf
, (size_t)ALPM_BUFFER_SIZE
, return 1);
157 OPEN(in
, src
, O_RDONLY
);
159 out
= open(dest
, O_WRONLY
| O_CREAT
, 0000);
160 } while(out
== -1 && errno
== EINTR
);
161 if(in
< 0 || out
< 0) {
165 if(fstat(in
, &st
) || fchmod(out
, st
.st_mode
)) {
169 /* do the actual file copy */
170 while((nread
= read(in
, buf
, ALPM_BUFFER_SIZE
)) > 0 || errno
== EINTR
) {
176 nwrite
= write(out
, buf
+ nwrite
, nread
);
179 } else if(errno
!= EINTR
) {
197 /** Trim trailing newlines from a string (if any exist).
198 * @param str a single line of text
199 * @param len size of str, if known, else 0
200 * @return the length of the trimmed string
202 size_t _alpm_strip_newline(char *str
, size_t len
)
210 while(len
> 0 && str
[len
- 1] == '\n') {
218 /* Compression functions */
220 /** Open an archive for reading and perform the necessary boilerplate.
221 * This takes care of creating the libarchive 'archive' struct, setting up
222 * compression and format options, opening a file descriptor, setting up the
223 * buffer size, and performing a stat on the path once opened.
224 * On error, no file descriptor is opened, and the archive pointer returned
225 * will be set to NULL.
226 * @param handle the context handle
227 * @param path the path of the archive to open
228 * @param buf space for a stat buffer for the given path
229 * @param archive pointer to place the created archive object
230 * @param error error code to set on failure to open archive
231 * @return -1 on failure, >=0 file descriptor on success
233 int _alpm_open_archive(alpm_handle_t
*handle
, const char *path
,
234 struct stat
*buf
, struct archive
**archive
, alpm_errno_t error
)
237 size_t bufsize
= ALPM_BUFFER_SIZE
;
240 if((*archive
= archive_read_new()) == NULL
) {
241 RET_ERR(handle
, ALPM_ERR_LIBARCHIVE
, -1);
244 _alpm_archive_read_support_filter_all(*archive
);
245 archive_read_support_format_all(*archive
);
247 _alpm_log(handle
, ALPM_LOG_DEBUG
, "opening archive %s\n", path
);
248 OPEN(fd
, path
, O_RDONLY
);
250 _alpm_log(handle
, ALPM_LOG_ERROR
,
251 _("could not open file %s: %s\n"), path
, strerror(errno
));
255 if(fstat(fd
, buf
) != 0) {
256 _alpm_log(handle
, ALPM_LOG_ERROR
,
257 _("could not stat file %s: %s\n"), path
, strerror(errno
));
260 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
261 if(buf
->st_blksize
> ALPM_BUFFER_SIZE
) {
262 bufsize
= buf
->st_blksize
;
266 if(archive_read_open_fd(*archive
, fd
, bufsize
) != ARCHIVE_OK
) {
267 _alpm_log(handle
, ALPM_LOG_ERROR
, _("could not open file %s: %s\n"),
268 path
, archive_error_string(*archive
));
275 _alpm_archive_read_free(*archive
);
280 RET_ERR(handle
, error
, -1);
283 /** Unpack a specific file in an archive.
284 * @param handle the context handle
285 * @param archive the archive to unpack
286 * @param prefix where to extract the files
287 * @param filename a file within the archive to unpack
288 * @return 0 on success, 1 on failure
290 int _alpm_unpack_single(alpm_handle_t
*handle
, const char *archive
,
291 const char *prefix
, const char *filename
)
293 alpm_list_t
*list
= NULL
;
295 if(filename
== NULL
) {
298 list
= alpm_list_add(list
, (void *)filename
);
299 ret
= _alpm_unpack(handle
, archive
, prefix
, list
, 1);
300 alpm_list_free(list
);
304 /** Unpack a list of files in an archive.
305 * @param handle the context handle
306 * @param path the archive to unpack
307 * @param prefix where to extract the files
308 * @param list a list of files within the archive to unpack or NULL for all
309 * @param breakfirst break after the first entry found
310 * @return 0 on success, 1 on failure
312 int _alpm_unpack(alpm_handle_t
*handle
, const char *path
, const char *prefix
,
313 alpm_list_t
*list
, int breakfirst
)
317 struct archive
*archive
;
318 struct archive_entry
*entry
;
322 fd
= _alpm_open_archive(handle
, path
, &buf
, &archive
, ALPM_ERR_PKG_OPEN
);
327 oldmask
= umask(0022);
329 /* save the cwd so we can restore it later */
330 OPEN(cwdfd
, ".", O_RDONLY
);
332 _alpm_log(handle
, ALPM_LOG_ERROR
, _("could not get current working directory\n"));
335 /* just in case our cwd was removed in the upgrade operation */
336 if(chdir(prefix
) != 0) {
337 _alpm_log(handle
, ALPM_LOG_ERROR
, _("could not change directory to %s (%s)\n"),
338 prefix
, strerror(errno
));
343 while(archive_read_next_header(archive
, &entry
) == ARCHIVE_OK
) {
344 const char *entryname
;
347 entryname
= archive_entry_pathname(entry
);
349 /* If specific files were requested, skip entries that don't match. */
351 char *entry_prefix
= strdup(entryname
);
352 char *p
= strstr(entry_prefix
,"/");
356 char *found
= alpm_list_find_str(list
, entry_prefix
);
359 if(archive_read_data_skip(archive
) != ARCHIVE_OK
) {
365 _alpm_log(handle
, ALPM_LOG_DEBUG
, "extracting: %s\n", entryname
);
369 mode
= archive_entry_mode(entry
);
371 archive_entry_set_perm(entry
, 0644);
372 } else if(S_ISDIR(mode
)) {
373 archive_entry_set_perm(entry
, 0755);
376 /* Extract the archive entry. */
377 int readret
= archive_read_extract(archive
, entry
, 0);
378 if(readret
== ARCHIVE_WARN
) {
379 /* operation succeeded but a non-critical error was encountered */
380 _alpm_log(handle
, ALPM_LOG_WARNING
, _("warning given when extracting %s (%s)\n"),
381 entryname
, archive_error_string(archive
));
382 } else if(readret
!= ARCHIVE_OK
) {
383 _alpm_log(handle
, ALPM_LOG_ERROR
, _("could not extract %s (%s)\n"),
384 entryname
, archive_error_string(archive
));
396 _alpm_archive_read_free(archive
);
399 if(fchdir(cwdfd
) != 0) {
400 _alpm_log(handle
, ALPM_LOG_ERROR
,
401 _("could not restore working directory (%s)\n"), strerror(errno
));
409 /** Determine if there are files in a directory.
410 * @param handle the context handle
411 * @param path the full absolute directory path
412 * @param full_count whether to return an exact count of files
413 * @return a file count if full_count is != 0, else >0 if directory has
414 * contents, 0 if no contents, and -1 on error
416 ssize_t
_alpm_files_in_directory(alpm_handle_t
*handle
, const char *path
,
421 DIR *dir
= opendir(path
);
424 if(errno
== ENOTDIR
) {
425 _alpm_log(handle
, ALPM_LOG_DEBUG
, "%s was not a directory\n", path
);
427 _alpm_log(handle
, ALPM_LOG_DEBUG
, "could not read directory %s\n",
432 while((ent
= readdir(dir
)) != NULL
) {
433 const char *name
= ent
->d_name
;
435 if(strcmp(name
, ".") == 0 || strcmp(name
, "..") == 0) {
450 /** Write formatted message to log.
451 * @param handle the context handle
452 * @param format formatted string to write out
453 * @param args formatting arguments
454 * @return 0 or number of characters written on success, vfprintf return value
457 int _alpm_logaction(alpm_handle_t
*handle
, const char *prefix
,
458 const char *fmt
, va_list args
)
462 if(!(prefix
&& *prefix
)) {
466 if(handle
->usesyslog
) {
467 /* we can't use a va_list more than once, so we need to copy it
468 * so we can use the original when calling vfprintf below. */
470 va_copy(args_syslog
, args
);
471 vsyslog(LOG_WARNING
, fmt
, args_syslog
);
475 if(handle
->logstream
) {
482 /* Use ISO-8601 date format */
483 fprintf(handle
->logstream
, "[%04d-%02d-%02d %02d:%02d] [%s] ",
484 tm
->tm_year
+ 1900, tm
->tm_mon
+ 1, tm
->tm_mday
,
485 tm
->tm_hour
, tm
->tm_min
, prefix
);
486 ret
= vfprintf(handle
->logstream
, fmt
, args
);
487 fflush(handle
->logstream
);
493 /** Execute a command with arguments in a chroot.
494 * @param handle the context handle
495 * @param cmd command to execute
496 * @param argv arguments to pass to cmd
497 * @return 0 on success, 1 on error
499 int _alpm_run_chroot(alpm_handle_t
*handle
, const char *cmd
, char *const argv
[])
502 int pipefd
[2], cwdfd
;
505 /* save the cwd so we can restore it later */
506 OPEN(cwdfd
, ".", O_RDONLY
);
508 _alpm_log(handle
, ALPM_LOG_ERROR
, _("could not get current working directory\n"));
511 /* just in case our cwd was removed in the upgrade operation */
512 if(chdir(handle
->root
) != 0) {
513 _alpm_log(handle
, ALPM_LOG_ERROR
, _("could not change directory to %s (%s)\n"),
514 handle
->root
, strerror(errno
));
518 _alpm_log(handle
, ALPM_LOG_DEBUG
, "executing \"%s\" under chroot \"%s\"\n",
521 /* Flush open fds before fork() to avoid cloning buffers */
524 if(pipe(pipefd
) == -1) {
525 _alpm_log(handle
, ALPM_LOG_ERROR
, _("could not create pipe (%s)\n"), strerror(errno
));
530 /* fork- parent and child each have separate code blocks below */
533 _alpm_log(handle
, ALPM_LOG_ERROR
, _("could not fork a new process (%s)\n"), strerror(errno
));
539 /* this code runs for the child only (the actual chroot/exec) */
542 while(dup2(pipefd
[1], 1) == -1 && errno
== EINTR
);
543 while(dup2(pipefd
[1], 2) == -1 && errno
== EINTR
);
547 /* use fprintf instead of _alpm_log to send output through the parent */
548 if(chroot(handle
->root
) != 0) {
549 fprintf(stderr
, _("could not change the root directory (%s)\n"), strerror(errno
));
552 if(chdir("/") != 0) {
553 fprintf(stderr
, _("could not change directory to %s (%s)\n"),
554 "/", strerror(errno
));
559 /* execv only returns if there was an error */
560 fprintf(stderr
, _("call to execv failed (%s)\n"), strerror(errno
));
563 /* this code runs for the parent only (wait on the child) */
568 pipe_file
= fdopen(pipefd
[0], "r");
569 if(pipe_file
== NULL
) {
573 while(!feof(pipe_file
)) {
575 if(fgets(line
, PATH_MAX
, pipe_file
) == NULL
)
577 alpm_logaction(handle
, "ALPM-SCRIPTLET", "%s", line
);
578 EVENT(handle
, ALPM_EVENT_SCRIPTLET_INFO
, line
, NULL
);
583 while(waitpid(pid
, &status
, 0) == -1) {
585 _alpm_log(handle
, ALPM_LOG_ERROR
, _("call to waitpid failed (%s)\n"), strerror(errno
));
591 /* report error from above after the child has exited */
593 _alpm_log(handle
, ALPM_LOG_ERROR
, _("could not open pipe (%s)\n"), strerror(errno
));
596 /* check the return status, make sure it is 0 (success) */
597 if(WIFEXITED(status
)) {
598 _alpm_log(handle
, ALPM_LOG_DEBUG
, "call to waitpid succeeded\n");
599 if(WEXITSTATUS(status
) != 0) {
600 _alpm_log(handle
, ALPM_LOG_ERROR
, _("command failed to execute correctly\n"));
608 if(fchdir(cwdfd
) != 0) {
609 _alpm_log(handle
, ALPM_LOG_ERROR
,
610 _("could not restore working directory (%s)\n"), strerror(errno
));
618 /** Run ldconfig in a chroot.
619 * @param handle the context handle
620 * @return 0 on success, 1 on error
622 int _alpm_ldconfig(alpm_handle_t
*handle
)
626 _alpm_log(handle
, ALPM_LOG_DEBUG
, "running ldconfig\n");
628 snprintf(line
, PATH_MAX
, "%setc/ld.so.conf", handle
->root
);
629 if(access(line
, F_OK
) == 0) {
630 snprintf(line
, PATH_MAX
, "%s%s", handle
->root
, LDCONFIG
);
631 if(access(line
, X_OK
) == 0) {
633 char *argv
[] = { arg0
, NULL
};
634 strcpy(arg0
, "ldconfig");
635 return _alpm_run_chroot(handle
, LDCONFIG
, argv
);
642 /** Helper function for comparing strings using the alpm "compare func"
644 * @param s1 first string to be compared
645 * @param s2 second string to be compared
646 * @return 0 if strings are equal, positive int if first unequal character
647 * has a greater value in s1, negative if it has a greater value in s2
649 int _alpm_str_cmp(const void *s1
, const void *s2
)
651 return strcmp(s1
, s2
);
654 /** Find a filename in a registered alpm cachedir.
655 * @param handle the context handle
656 * @param filename name of file to find
657 * @return malloced path of file, NULL if not found
659 char *_alpm_filecache_find(alpm_handle_t
*handle
, const char *filename
)
666 /* Loop through the cache dirs until we find a matching file */
667 for(i
= handle
->cachedirs
; i
; i
= i
->next
) {
668 snprintf(path
, PATH_MAX
, "%s%s", (char *)i
->data
,
670 if(stat(path
, &buf
) == 0 && S_ISREG(buf
.st_mode
)) {
671 retpath
= strdup(path
);
672 _alpm_log(handle
, ALPM_LOG_DEBUG
, "found cached pkg: %s\n", retpath
);
676 /* package wasn't found in any cachedir */
680 /** Check the alpm cachedirs for existence and find a writable one.
681 * If no valid cache directory can be found, use /tmp.
682 * @param handle the context handle
683 * @return pointer to a writable cache directory.
685 const char *_alpm_filecache_setup(alpm_handle_t
*handle
)
692 /* Loop through the cache dirs until we find a usable directory */
693 for(i
= handle
->cachedirs
; i
; i
= i
->next
) {
695 if(stat(cachedir
, &buf
) != 0) {
696 /* cache directory does not exist.... try creating it */
697 _alpm_log(handle
, ALPM_LOG_WARNING
, _("no %s cache exists, creating...\n"),
699 if(_alpm_makepath(cachedir
) == 0) {
700 _alpm_log(handle
, ALPM_LOG_DEBUG
, "using cachedir: %s\n", cachedir
);
703 } else if(!S_ISDIR(buf
.st_mode
)) {
704 _alpm_log(handle
, ALPM_LOG_DEBUG
,
705 "skipping cachedir, not a directory: %s\n", cachedir
);
706 } else if(_alpm_access(handle
, NULL
, cachedir
, W_OK
) != 0) {
707 _alpm_log(handle
, ALPM_LOG_DEBUG
,
708 "skipping cachedir, not writable: %s\n", cachedir
);
709 } else if(!(buf
.st_mode
& (S_IWUSR
| S_IWGRP
| S_IWOTH
))) {
710 _alpm_log(handle
, ALPM_LOG_DEBUG
,
711 "skipping cachedir, no write bits set: %s\n", cachedir
);
713 _alpm_log(handle
, ALPM_LOG_DEBUG
, "using cachedir: %s\n", cachedir
);
718 /* we didn't find a valid cache directory. use TMPDIR or /tmp. */
719 if((tmpdir
= getenv("TMPDIR")) && stat(tmpdir
, &buf
) && S_ISDIR(buf
.st_mode
)) {
720 /* TMPDIR was good, we can use it */
724 alpm_option_add_cachedir(handle
, tmpdir
);
725 cachedir
= handle
->cachedirs
->prev
->data
;
726 _alpm_log(handle
, ALPM_LOG_DEBUG
, "using cachedir: %s\n", cachedir
);
727 _alpm_log(handle
, ALPM_LOG_WARNING
,
728 _("couldn't find or create package cache, using %s instead\n"), cachedir
);
732 /** lstat wrapper that treats /path/dirsymlink/ the same as /path/dirsymlink.
733 * Linux lstat follows POSIX semantics and still performs a dereference on
734 * the first, and for uses of lstat in libalpm this is not what we want.
735 * @param path path to file to lstat
736 * @param buf structure to fill with stat information
737 * @return the return code from lstat
739 int _alpm_lstat(const char *path
, struct stat
*buf
)
742 size_t len
= strlen(path
);
744 /* strip the trailing slash if one exists */
745 if(len
!= 0 && path
[len
- 1] == '/') {
746 char *newpath
= strdup(path
);
747 newpath
[len
- 1] = '\0';
748 ret
= lstat(newpath
, buf
);
751 ret
= lstat(path
, buf
);
758 /** Compute the MD5 message digest of a file.
759 * @param path file path of file to compute MD5 digest of
760 * @param output string to hold computed MD5 digest
761 * @return 0 on success, 1 on file open error, 2 on file read error
763 static int md5_file(const char *path
, unsigned char output
[16])
770 MALLOC(buf
, (size_t)ALPM_BUFFER_SIZE
, return 1);
772 OPEN(fd
, path
, O_RDONLY
);
780 while((n
= read(fd
, buf
, ALPM_BUFFER_SIZE
)) > 0 || errno
== EINTR
) {
784 MD5_Update(&ctx
, buf
, n
);
794 MD5_Final(output
, &ctx
);
798 /* third param is so we match the PolarSSL definition */
799 /** Compute the SHA-224 or SHA-256 message digest of a file.
800 * @param path file path of file to compute SHA2 digest of
801 * @param output string to hold computed SHA2 digest
802 * @param is224 use SHA-224 instead of SHA-256
803 * @return 0 on success, 1 on file open error, 2 on file read error
805 static int sha2_file(const char *path
, unsigned char output
[32], int is224
)
812 MALLOC(buf
, (size_t)ALPM_BUFFER_SIZE
, return 1);
814 OPEN(fd
, path
, O_RDONLY
);
826 while((n
= read(fd
, buf
, ALPM_BUFFER_SIZE
)) > 0 || errno
== EINTR
) {
831 SHA224_Update(&ctx
, buf
, n
);
833 SHA256_Update(&ctx
, buf
, n
);
845 SHA224_Final(output
, &ctx
);
847 SHA256_Final(output
, &ctx
);
853 /** Create a string representing bytes in hexadecimal.
854 * @param bytes the bytes to represent in hexadecimal
855 * @param size number of bytes to consider
856 * @return a NULL terminated string with the hexadecimal representation of
857 * bytes or NULL on error. This string must be freed.
859 static char *hex_representation(unsigned char *bytes
, size_t size
)
861 static const char *hex_digits
= "0123456789abcdef";
865 MALLOC(str
, 2 * size
+ 1, return NULL
);
867 for(i
= 0; i
< size
; i
++) {
868 str
[2 * i
] = hex_digits
[bytes
[i
] >> 4];
869 str
[2 * i
+ 1] = hex_digits
[bytes
[i
] & 0x0f];
872 str
[2 * size
] = '\0';
877 /** Get the md5 sum of file.
878 * @param filename name of the file
879 * @return the checksum on success, NULL on error
880 * @addtogroup alpm_misc
882 char SYMEXPORT
*alpm_compute_md5sum(const char *filename
)
884 unsigned char output
[16];
886 ASSERT(filename
!= NULL
, return NULL
);
888 /* defined above for OpenSSL, otherwise defined in md5.h */
889 if(md5_file(filename
, output
) > 0) {
893 return hex_representation(output
, 16);
896 /** Get the sha256 sum of file.
897 * @param filename name of the file
898 * @return the checksum on success, NULL on error
899 * @addtogroup alpm_misc
901 char SYMEXPORT
*alpm_compute_sha256sum(const char *filename
)
903 unsigned char output
[32];
905 ASSERT(filename
!= NULL
, return NULL
);
907 /* defined above for OpenSSL, otherwise defined in sha2.h */
908 if(sha2_file(filename
, output
, 0) > 0) {
912 return hex_representation(output
, 32);
915 /** Calculates a file's MD5 or SHA2 digest and compares it to an expected value.
916 * @param filepath path of the file to check
917 * @param expected hash value to compare against
918 * @param type digest type to use
919 * @return 0 if file matches the expected hash, 1 if they do not match, -1 on
922 int _alpm_test_checksum(const char *filepath
, const char *expected
,
923 alpm_pkgvalidation_t type
)
928 if(type
== ALPM_PKG_VALIDATION_MD5SUM
) {
929 computed
= alpm_compute_md5sum(filepath
);
930 } else if(type
== ALPM_PKG_VALIDATION_SHA256SUM
) {
931 computed
= alpm_compute_sha256sum(filepath
);
936 if(expected
== NULL
|| computed
== NULL
) {
938 } else if(strcmp(expected
, computed
) != 0) {
948 /* Note: does NOT handle sparse files on purpose for speed. */
950 * Does not handle sparse files on purpose for speed.
955 int _alpm_archive_fgets(struct archive
*a
, struct archive_read_buffer
*b
)
957 /* ensure we start populating our line buffer at the beginning */
958 b
->line_offset
= b
->line
;
961 size_t block_remaining
;
964 /* have we processed this entire block? */
965 if(b
->block
+ b
->block_size
== b
->block_offset
) {
967 if(b
->ret
== ARCHIVE_EOF
) {
968 /* reached end of archive on the last read, now we are out of data */
972 /* zero-copy - this is the entire next block of data. */
973 b
->ret
= archive_read_data_block(a
, (void *)&b
->block
,
974 &b
->block_size
, &offset
);
975 b
->block_offset
= b
->block
;
976 block_remaining
= b
->block_size
;
979 if(b
->ret
< ARCHIVE_OK
) {
983 block_remaining
= b
->block
+ b
->block_size
- b
->block_offset
;
986 /* look through the block looking for EOL characters */
987 eol
= memchr(b
->block_offset
, '\n', block_remaining
);
989 eol
= memchr(b
->block_offset
, '\0', block_remaining
);
992 /* allocate our buffer, or ensure our existing one is big enough */
994 /* set the initial buffer to the read block_size */
995 CALLOC(b
->line
, b
->block_size
+ 1, sizeof(char), b
->ret
= -ENOMEM
; goto cleanup
);
996 b
->line_size
= b
->block_size
+ 1;
997 b
->line_offset
= b
->line
;
999 /* note: we know eol > b->block_offset and b->line_offset > b->line,
1000 * so we know the result is unsigned and can fit in size_t */
1001 size_t new = eol
? (size_t)(eol
- b
->block_offset
) : block_remaining
;
1002 size_t needed
= (size_t)((b
->line_offset
- b
->line
) + new + 1);
1003 if(needed
> b
->max_line_size
) {
1007 if(needed
> b
->line_size
) {
1008 /* need to realloc + copy data to fit total length */
1010 CALLOC(new_line
, needed
, sizeof(char), b
->ret
= -ENOMEM
; goto cleanup
);
1011 memcpy(new_line
, b
->line
, b
->line_size
);
1012 b
->line_size
= needed
;
1013 b
->line_offset
= new_line
+ (b
->line_offset
- b
->line
);
1020 size_t len
= (size_t)(eol
- b
->block_offset
);
1021 memcpy(b
->line_offset
, b
->block_offset
, len
);
1022 b
->line_offset
[len
] = '\0';
1023 b
->block_offset
= eol
+ 1;
1024 b
->real_line_size
= b
->line_offset
+ len
- b
->line
;
1025 /* this is the main return point; from here you can read b->line */
1028 /* we've looked through the whole block but no newline, copy it */
1029 size_t len
= (size_t)(b
->block
+ b
->block_size
- b
->block_offset
);
1030 memcpy(b
->line_offset
, b
->block_offset
, len
);
1031 b
->line_offset
+= len
;
1032 b
->block_offset
= b
->block
+ b
->block_size
;
1033 /* there was no new data, return what is left; saved ARCHIVE_EOF will be
1034 * returned on next call */
1036 b
->line_offset
[0] = '\0';
1037 b
->real_line_size
= b
->line_offset
- b
->line
;
1047 memset(b
, 0, sizeof(struct archive_read_buffer
));
1052 /** Parse a full package specifier.
1053 * @param target package specifier to parse, such as: "pacman-4.0.1-2",
1054 * "pacman-4.01-2/", or "pacman-4.0.1-2/desc"
1055 * @param name to hold package name
1056 * @param version to hold package version
1057 * @param name_hash to hold package name hash
1058 * @return 0 on success, -1 on error
1060 int _alpm_splitname(const char *target
, char **name
, char **version
,
1061 unsigned long *name_hash
)
1063 /* the format of a db entry is as follows:
1064 * package-version-rel/
1065 * package-version-rel/desc (we ignore the filename portion)
1066 * package name can contain hyphens, so parse from the back- go back
1067 * two hyphens and we have split the version from the name.
1069 const char *pkgver
, *end
;
1071 if(target
== NULL
) {
1075 /* remove anything trailing a '/' */
1076 end
= strchr(target
, '/');
1078 end
= target
+ strlen(target
);
1081 /* do the magic parsing- find the beginning of the version string
1082 * by doing two iterations of same loop to lop off two hyphens */
1083 for(pkgver
= end
- 1; *pkgver
&& *pkgver
!= '-'; pkgver
--);
1084 for(pkgver
= pkgver
- 1; *pkgver
&& *pkgver
!= '-'; pkgver
--);
1085 if(*pkgver
!= '-' || pkgver
== target
) {
1089 /* copy into fields and return */
1094 /* version actually points to the dash, so need to increment 1 and account
1095 * for potential end character */
1096 STRNDUP(*version
, pkgver
+ 1, end
- pkgver
- 1, return -1);
1103 STRNDUP(*name
, target
, pkgver
- target
, return -1);
1105 *name_hash
= _alpm_hash_sdbm(*name
);
1112 /** Hash the given string to an unsigned long value.
1113 * This is the standard sdbm hashing algorithm.
1114 * @param str string to hash
1115 * @return the hash value of the given string
1117 unsigned long _alpm_hash_sdbm(const char *str
)
1119 unsigned long hash
= 0;
1125 while((c
= *str
++)) {
1126 hash
= c
+ hash
* 65599;
1132 /** Convert a string to a file offset.
1133 * This parses bare positive integers only.
1134 * @param line string to convert
1135 * @return off_t on success, -1 on error
1137 off_t
_alpm_strtoofft(const char *line
)
1140 unsigned long long result
;
1143 /* we are trying to parse bare numbers only, no leading anything */
1144 if(!isdigit((unsigned char)line
[0])) {
1147 result
= strtoull(line
, &end
, 10);
1148 if(result
== 0 && end
== line
) {
1149 /* line was not a number */
1151 } else if(result
== ULLONG_MAX
&& errno
== ERANGE
) {
1152 /* line does not fit in unsigned long long */
1155 /* line began with a number but has junk left over at the end */
1159 return (off_t
)result
;
1162 /** Parses a date into an alpm_time_t struct.
1163 * @param line date to parse
1164 * @return time struct on success, 0 on error
1166 alpm_time_t
_alpm_parsedate(const char *line
)
1172 if(isalpha((unsigned char)line
[0])) {
1173 const char *oldlocale
;
1174 /* initialize to null in case of failure */
1176 memset(&tmp_tm
, 0, sizeof(struct tm
));
1178 oldlocale
= setlocale(LC_TIME
, NULL
);
1179 setlocale(LC_TIME
, "C");
1180 strptime(line
, "%a %b %e %H:%M:%S %Y", &tmp_tm
);
1181 setlocale(LC_TIME
, oldlocale
);
1183 return (alpm_time_t
)mktime(&tmp_tm
);
1186 result
= strtoll(line
, &end
, 10);
1187 if(result
== 0 && end
== line
) {
1188 /* line was not a number */
1191 } else if(errno
== ERANGE
) {
1192 /* line does not fit in long long */
1195 /* line began with a number but has junk left over at the end */
1200 return (alpm_time_t
)result
;
1203 /** Wrapper around access() which takes a dir and file argument
1204 * separately and generates an appropriate error message.
1205 * If dir is NULL file will be treated as the whole path.
1206 * @param handle an alpm handle
1207 * @param dir directory path ending with and slash
1208 * @param file filename
1209 * @param amode access mode as described in access()
1210 * @return int value returned by access()
1212 int _alpm_access(alpm_handle_t
*handle
, const char *dir
, const char *file
, int amode
)
1220 len
= strlen(dir
) + strlen(file
) + 1;
1221 CALLOC(check_path
, len
, sizeof(char), RET_ERR(handle
, ALPM_ERR_MEMORY
, -1));
1222 snprintf(check_path
, len
, "%s%s", dir
, file
);
1224 ret
= access(check_path
, amode
);
1228 ret
= access(file
, amode
);
1233 _alpm_log(handle
, ALPM_LOG_DEBUG
, "\"%s%s\" is not readable: %s\n",
1234 dir
, file
, strerror(errno
));
1237 _alpm_log(handle
, ALPM_LOG_DEBUG
, "\"%s%s\" is not writable: %s\n",
1238 dir
, file
, strerror(errno
));
1241 _alpm_log(handle
, ALPM_LOG_DEBUG
, "\"%s%s\" is not executable: %s\n",
1242 dir
, file
, strerror(errno
));
1245 _alpm_log(handle
, ALPM_LOG_DEBUG
, "\"%s%s\" does not exist: %s\n",
1246 dir
, file
, strerror(errno
));
1252 /** Checks whether a string matches at least one shell wildcard pattern.
1253 * Checks for matches with fnmatch. Matches are inverted by prepending
1254 * patterns with an exclamation mark. Preceding exclamation marks may be
1255 * escaped. Subsequent matches override previous ones.
1256 * @param patterns patterns to match against
1257 * @param string string to check against pattern
1258 * @return 0 if string matches pattern, negative if they don't match and
1259 * positive if the last match was inverted
1261 int _alpm_fnmatch_patterns(alpm_list_t
*patterns
, const char *string
)
1267 for(i
= alpm_list_last(patterns
); i
; i
= alpm_list_previous(i
)) {
1270 inverted
= pattern
[0] == '!';
1271 if(inverted
|| pattern
[0] == '\\') {
1275 if(_alpm_fnmatch(pattern
, string
) == 0) {
1283 /** Checks whether a string matches a shell wildcard pattern.
1284 * Wrapper around fnmatch.
1285 * @param pattern pattern to match against
1286 * @param string string to check against pattern
1287 * @return 0 if string matches pattern, non-zero if they don't match and on
1290 int _alpm_fnmatch(const void *pattern
, const void *string
)
1292 return fnmatch(pattern
, string
, 0);
1295 void _alpm_alloc_fail(size_t size
)
1297 fprintf(stderr
, "alloc failure: could not allocate %zd bytes\n", size
);
1300 /* vim: set ts=2 sw=2 noet: */