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/>.
38 #include <archive_entry.h>
41 #include <openssl/md5.h>
42 #include <openssl/sha.h>
51 #include "libarchive-compat.h"
53 #include "alpm_list.h"
58 /** Extracts tokens from a string.
59 * Replaces strset which is not portable (missing on Solaris).
60 * Copyright (c) 2001 by François Gouget <fgouget_at_codeweavers.com>
61 * Modifies str to point to the first character after the token if one is
62 * found, or NULL if one is not.
63 * @param str string containing delimited tokens to parse
64 * @param delim character delimiting tokens in str
65 * @return pointer to the first token in str if str is not NULL, NULL if
68 char *strsep(char **str
, const char *delims
)
78 while(**str
!= '\0') {
79 if(strchr(delims
, **str
) != NULL
) {
86 /* There is no other token */
92 int _alpm_makepath(const char *path
)
94 return _alpm_makepath_mode(path
, 0755);
97 /** Creates a directory, including parents if needed, similar to 'mkdir -p'.
98 * @param path directory path to create
99 * @param mode permission mode for created directories
100 * @return 0 on success, 1 on error
102 int _alpm_makepath_mode(const char *path
, mode_t mode
)
108 STRDUP(str
, path
, return 1);
110 oldmask
= umask(0000);
112 for(ptr
= str
; *ptr
; ptr
++) {
113 /* detect mid-path condition and zero length paths */
114 if(*ptr
!= '/' || ptr
== str
|| ptr
[-1] == '/') {
118 /* temporarily mask the end of the path */
121 if(mkdir(str
, mode
) < 0 && errno
!= EEXIST
) {
126 /* restore path separator */
130 /* end of the string. add the full path. It will already exist when the path
131 * passed in has a trailing slash. */
132 if(mkdir(str
, mode
) < 0 && errno
!= EEXIST
) {
143 * @param src file path to copy from
144 * @param dest file path to copy to
145 * @return 0 on success, 1 on error
147 int _alpm_copyfile(const char *src
, const char *dest
)
150 int in
, out
, ret
= 1;
154 MALLOC(buf
, (size_t)ALPM_BUFFER_SIZE
, return 1);
156 OPEN(in
, src
, O_RDONLY
);
158 out
= open(dest
, O_WRONLY
| O_CREAT
, 0000);
159 } while(out
== -1 && errno
== EINTR
);
160 if(in
< 0 || out
< 0) {
164 if(fstat(in
, &st
) || fchmod(out
, st
.st_mode
)) {
168 /* do the actual file copy */
169 while((nread
= read(in
, buf
, ALPM_BUFFER_SIZE
)) > 0 || errno
== EINTR
) {
175 nwrite
= write(out
, buf
+ nwrite
, nread
);
178 } else if(errno
!= EINTR
) {
196 /** Trim trailing newlines from a string (if any exist).
197 * @param str a single line of text
198 * @param len size of str, if known, else 0
199 * @return the length of the trimmed string
201 size_t _alpm_strip_newline(char *str
, size_t len
)
209 while(len
> 0 && str
[len
- 1] == '\n') {
217 /* Compression functions */
219 /** Open an archive for reading and perform the necessary boilerplate.
220 * This takes care of creating the libarchive 'archive' struct, setting up
221 * compression and format options, opening a file descriptor, setting up the
222 * buffer size, and performing a stat on the path once opened.
223 * On error, no file descriptor is opened, and the archive pointer returned
224 * will be set to NULL.
225 * @param handle the context handle
226 * @param path the path of the archive to open
227 * @param buf space for a stat buffer for the given path
228 * @param archive pointer to place the created archive object
229 * @param error error code to set on failure to open archive
230 * @return -1 on failure, >=0 file descriptor on success
232 int _alpm_open_archive(alpm_handle_t
*handle
, const char *path
,
233 struct stat
*buf
, struct archive
**archive
, alpm_errno_t error
)
236 size_t bufsize
= ALPM_BUFFER_SIZE
;
239 if((*archive
= archive_read_new()) == NULL
) {
240 RET_ERR(handle
, ALPM_ERR_LIBARCHIVE
, -1);
243 _alpm_archive_read_support_filter_all(*archive
);
244 archive_read_support_format_all(*archive
);
246 _alpm_log(handle
, ALPM_LOG_DEBUG
, "opening archive %s\n", path
);
247 OPEN(fd
, path
, O_RDONLY
);
249 _alpm_log(handle
, ALPM_LOG_ERROR
,
250 _("could not open file %s: %s\n"), path
, strerror(errno
));
254 if(fstat(fd
, buf
) != 0) {
255 _alpm_log(handle
, ALPM_LOG_ERROR
,
256 _("could not stat file %s: %s\n"), path
, strerror(errno
));
259 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
260 if(buf
->st_blksize
> ALPM_BUFFER_SIZE
) {
261 bufsize
= buf
->st_blksize
;
265 if(archive_read_open_fd(*archive
, fd
, bufsize
) != ARCHIVE_OK
) {
266 _alpm_log(handle
, ALPM_LOG_ERROR
, _("could not open file %s: %s\n"),
267 path
, archive_error_string(*archive
));
274 _alpm_archive_read_free(*archive
);
279 RET_ERR(handle
, error
, -1);
282 /** Unpack a specific file in an archive.
283 * @param handle the context handle
284 * @param archive the archive to unpack
285 * @param prefix where to extract the files
286 * @param filename a file within the archive to unpack
287 * @return 0 on success, 1 on failure
289 int _alpm_unpack_single(alpm_handle_t
*handle
, const char *archive
,
290 const char *prefix
, const char *filename
)
292 alpm_list_t
*list
= NULL
;
294 if(filename
== NULL
) {
297 list
= alpm_list_add(list
, (void *)filename
);
298 ret
= _alpm_unpack(handle
, archive
, prefix
, list
, 1);
299 alpm_list_free(list
);
303 /** Unpack a list of files in an archive.
304 * @param handle the context handle
305 * @param path the archive to unpack
306 * @param prefix where to extract the files
307 * @param list a list of files within the archive to unpack or NULL for all
308 * @param breakfirst break after the first entry found
309 * @return 0 on success, 1 on failure
311 int _alpm_unpack(alpm_handle_t
*handle
, const char *path
, const char *prefix
,
312 alpm_list_t
*list
, int breakfirst
)
316 struct archive
*archive
;
317 struct archive_entry
*entry
;
321 fd
= _alpm_open_archive(handle
, path
, &buf
, &archive
, ALPM_ERR_PKG_OPEN
);
326 oldmask
= umask(0022);
328 /* save the cwd so we can restore it later */
329 OPEN(cwdfd
, ".", O_RDONLY
);
331 _alpm_log(handle
, ALPM_LOG_ERROR
, _("could not get current working directory\n"));
334 /* just in case our cwd was removed in the upgrade operation */
335 if(chdir(prefix
) != 0) {
336 _alpm_log(handle
, ALPM_LOG_ERROR
, _("could not change directory to %s (%s)\n"),
337 prefix
, strerror(errno
));
342 while(archive_read_next_header(archive
, &entry
) == ARCHIVE_OK
) {
343 const char *entryname
;
346 entryname
= archive_entry_pathname(entry
);
348 /* If specific files were requested, skip entries that don't match. */
350 char *entry_prefix
= strdup(entryname
);
351 char *p
= strstr(entry_prefix
,"/");
355 char *found
= alpm_list_find_str(list
, entry_prefix
);
358 if(archive_read_data_skip(archive
) != ARCHIVE_OK
) {
364 _alpm_log(handle
, ALPM_LOG_DEBUG
, "extracting: %s\n", entryname
);
368 mode
= archive_entry_mode(entry
);
370 archive_entry_set_perm(entry
, 0644);
371 } else if(S_ISDIR(mode
)) {
372 archive_entry_set_perm(entry
, 0755);
375 /* Extract the archive entry. */
376 int readret
= archive_read_extract(archive
, entry
, 0);
377 if(readret
== ARCHIVE_WARN
) {
378 /* operation succeeded but a non-critical error was encountered */
379 _alpm_log(handle
, ALPM_LOG_WARNING
, _("warning given when extracting %s (%s)\n"),
380 entryname
, archive_error_string(archive
));
381 } else if(readret
!= ARCHIVE_OK
) {
382 _alpm_log(handle
, ALPM_LOG_ERROR
, _("could not extract %s (%s)\n"),
383 entryname
, archive_error_string(archive
));
395 _alpm_archive_read_free(archive
);
398 if(fchdir(cwdfd
) != 0) {
399 _alpm_log(handle
, ALPM_LOG_ERROR
,
400 _("could not restore working directory (%s)\n"), strerror(errno
));
408 /** Determine if there are files in a directory.
409 * @param handle the context handle
410 * @param path the full absolute directory path
411 * @param full_count whether to return an exact count of files
412 * @return a file count if full_count is != 0, else >0 if directory has
413 * contents, 0 if no contents, and -1 on error
415 ssize_t
_alpm_files_in_directory(alpm_handle_t
*handle
, const char *path
,
420 DIR *dir
= opendir(path
);
423 if(errno
== ENOTDIR
) {
424 _alpm_log(handle
, ALPM_LOG_DEBUG
, "%s was not a directory\n", path
);
426 _alpm_log(handle
, ALPM_LOG_DEBUG
, "could not read directory %s\n",
431 while((ent
= readdir(dir
)) != NULL
) {
432 const char *name
= ent
->d_name
;
434 if(strcmp(name
, ".") == 0 || strcmp(name
, "..") == 0) {
449 /** Write formatted message to log.
450 * @param handle the context handle
451 * @param format formatted string to write out
452 * @param args formatting arguments
453 * @return 0 or number of characters written on success, vfprintf return value
456 int _alpm_logaction(alpm_handle_t
*handle
, const char *prefix
,
457 const char *fmt
, va_list args
)
461 if(!(prefix
&& *prefix
)) {
465 if(handle
->usesyslog
) {
466 /* we can't use a va_list more than once, so we need to copy it
467 * so we can use the original when calling vfprintf below. */
469 va_copy(args_syslog
, args
);
470 vsyslog(LOG_WARNING
, fmt
, args_syslog
);
474 if(handle
->logstream
) {
481 /* Use ISO-8601 date format */
482 fprintf(handle
->logstream
, "[%04d-%02d-%02d %02d:%02d] [%s] ",
483 tm
->tm_year
+ 1900, tm
->tm_mon
+ 1, tm
->tm_mday
,
484 tm
->tm_hour
, tm
->tm_min
, prefix
);
485 ret
= vfprintf(handle
->logstream
, fmt
, args
);
486 fflush(handle
->logstream
);
492 /** Execute a command with arguments in a chroot.
493 * @param handle the context handle
494 * @param cmd command to execute
495 * @param argv arguments to pass to cmd
496 * @return 0 on success, 1 on error
498 int _alpm_run_chroot(alpm_handle_t
*handle
, const char *cmd
, char *const argv
[])
501 int pipefd
[2], cwdfd
;
504 /* save the cwd so we can restore it later */
505 OPEN(cwdfd
, ".", O_RDONLY
);
507 _alpm_log(handle
, ALPM_LOG_ERROR
, _("could not get current working directory\n"));
510 /* just in case our cwd was removed in the upgrade operation */
511 if(chdir(handle
->root
) != 0) {
512 _alpm_log(handle
, ALPM_LOG_ERROR
, _("could not change directory to %s (%s)\n"),
513 handle
->root
, strerror(errno
));
517 _alpm_log(handle
, ALPM_LOG_DEBUG
, "executing \"%s\" under chroot \"%s\"\n",
520 /* Flush open fds before fork() to avoid cloning buffers */
523 if(pipe(pipefd
) == -1) {
524 _alpm_log(handle
, ALPM_LOG_ERROR
, _("could not create pipe (%s)\n"), strerror(errno
));
529 /* fork- parent and child each have separate code blocks below */
532 _alpm_log(handle
, ALPM_LOG_ERROR
, _("could not fork a new process (%s)\n"), strerror(errno
));
538 /* this code runs for the child only (the actual chroot/exec) */
541 while(dup2(pipefd
[1], 1) == -1 && errno
== EINTR
);
542 while(dup2(pipefd
[1], 2) == -1 && errno
== EINTR
);
546 /* use fprintf instead of _alpm_log to send output through the parent */
547 if(chroot(handle
->root
) != 0) {
548 fprintf(stderr
, _("could not change the root directory (%s)\n"), strerror(errno
));
551 if(chdir("/") != 0) {
552 fprintf(stderr
, _("could not change directory to %s (%s)\n"),
553 "/", strerror(errno
));
558 /* execv only returns if there was an error */
559 fprintf(stderr
, _("call to execv failed (%s)\n"), strerror(errno
));
562 /* this code runs for the parent only (wait on the child) */
567 pipe_file
= fdopen(pipefd
[0], "r");
568 if(pipe_file
== NULL
) {
572 while(!feof(pipe_file
)) {
574 if(fgets(line
, PATH_MAX
, pipe_file
) == NULL
)
576 alpm_logaction(handle
, "ALPM-SCRIPTLET", "%s", line
);
577 EVENT(handle
, ALPM_EVENT_SCRIPTLET_INFO
, line
, NULL
);
582 while(waitpid(pid
, &status
, 0) == -1) {
584 _alpm_log(handle
, ALPM_LOG_ERROR
, _("call to waitpid failed (%s)\n"), strerror(errno
));
590 /* report error from above after the child has exited */
592 _alpm_log(handle
, ALPM_LOG_ERROR
, _("could not open pipe (%s)\n"), strerror(errno
));
595 /* check the return status, make sure it is 0 (success) */
596 if(WIFEXITED(status
)) {
597 _alpm_log(handle
, ALPM_LOG_DEBUG
, "call to waitpid succeeded\n");
598 if(WEXITSTATUS(status
) != 0) {
599 _alpm_log(handle
, ALPM_LOG_ERROR
, _("command failed to execute correctly\n"));
607 if(fchdir(cwdfd
) != 0) {
608 _alpm_log(handle
, ALPM_LOG_ERROR
,
609 _("could not restore working directory (%s)\n"), strerror(errno
));
617 /** Run ldconfig in a chroot.
618 * @param handle the context handle
619 * @return 0 on success, 1 on error
621 int _alpm_ldconfig(alpm_handle_t
*handle
)
625 _alpm_log(handle
, ALPM_LOG_DEBUG
, "running ldconfig\n");
627 snprintf(line
, PATH_MAX
, "%setc/ld.so.conf", handle
->root
);
628 if(access(line
, F_OK
) == 0) {
629 snprintf(line
, PATH_MAX
, "%s%s", handle
->root
, LDCONFIG
);
630 if(access(line
, X_OK
) == 0) {
632 char *argv
[] = { arg0
, NULL
};
633 strcpy(arg0
, "ldconfig");
634 return _alpm_run_chroot(handle
, LDCONFIG
, argv
);
641 /** Helper function for comparing strings using the alpm "compare func"
643 * @param s1 first string to be compared
644 * @param s2 second string to be compared
645 * @return 0 if strings are equal, positive int if first unequal character
646 * has a greater value in s1, negative if it has a greater value in s2
648 int _alpm_str_cmp(const void *s1
, const void *s2
)
650 return strcmp(s1
, s2
);
653 /** Find a filename in a registered alpm cachedir.
654 * @param handle the context handle
655 * @param filename name of file to find
656 * @return malloced path of file, NULL if not found
658 char *_alpm_filecache_find(alpm_handle_t
*handle
, const char *filename
)
665 /* Loop through the cache dirs until we find a matching file */
666 for(i
= handle
->cachedirs
; i
; i
= i
->next
) {
667 snprintf(path
, PATH_MAX
, "%s%s", (char *)i
->data
,
669 if(stat(path
, &buf
) == 0 && S_ISREG(buf
.st_mode
)) {
670 retpath
= strdup(path
);
671 _alpm_log(handle
, ALPM_LOG_DEBUG
, "found cached pkg: %s\n", retpath
);
675 /* package wasn't found in any cachedir */
679 /** Check the alpm cachedirs for existence and find a writable one.
680 * If no valid cache directory can be found, use /tmp.
681 * @param handle the context handle
682 * @return pointer to a writable cache directory.
684 const char *_alpm_filecache_setup(alpm_handle_t
*handle
)
691 /* Loop through the cache dirs until we find a usable directory */
692 for(i
= handle
->cachedirs
; i
; i
= i
->next
) {
694 if(stat(cachedir
, &buf
) != 0) {
695 /* cache directory does not exist.... try creating it */
696 _alpm_log(handle
, ALPM_LOG_WARNING
, _("no %s cache exists, creating...\n"),
698 if(_alpm_makepath(cachedir
) == 0) {
699 _alpm_log(handle
, ALPM_LOG_DEBUG
, "using cachedir: %s\n", cachedir
);
702 } else if(!S_ISDIR(buf
.st_mode
)) {
703 _alpm_log(handle
, ALPM_LOG_DEBUG
,
704 "skipping cachedir, not a directory: %s\n", cachedir
);
705 } else if(_alpm_access(handle
, NULL
, cachedir
, W_OK
) != 0) {
706 _alpm_log(handle
, ALPM_LOG_DEBUG
,
707 "skipping cachedir, not writable: %s\n", cachedir
);
708 } else if(!(buf
.st_mode
& (S_IWUSR
| S_IWGRP
| S_IWOTH
))) {
709 _alpm_log(handle
, ALPM_LOG_DEBUG
,
710 "skipping cachedir, no write bits set: %s\n", cachedir
);
712 _alpm_log(handle
, ALPM_LOG_DEBUG
, "using cachedir: %s\n", cachedir
);
717 /* we didn't find a valid cache directory. use TMPDIR or /tmp. */
718 if((tmpdir
= getenv("TMPDIR")) && stat(tmpdir
, &buf
) && S_ISDIR(buf
.st_mode
)) {
719 /* TMPDIR was good, we can use it */
723 alpm_option_add_cachedir(handle
, tmpdir
);
724 cachedir
= handle
->cachedirs
->prev
->data
;
725 _alpm_log(handle
, ALPM_LOG_DEBUG
, "using cachedir: %s\n", cachedir
);
726 _alpm_log(handle
, ALPM_LOG_WARNING
,
727 _("couldn't find or create package cache, using %s instead\n"), cachedir
);
731 /** lstat wrapper that treats /path/dirsymlink/ the same as /path/dirsymlink.
732 * Linux lstat follows POSIX semantics and still performs a dereference on
733 * the first, and for uses of lstat in libalpm this is not what we want.
734 * @param path path to file to lstat
735 * @param buf structure to fill with stat information
736 * @return the return code from lstat
738 int _alpm_lstat(const char *path
, struct stat
*buf
)
741 size_t len
= strlen(path
);
743 /* strip the trailing slash if one exists */
744 if(len
!= 0 && path
[len
- 1] == '/') {
745 char *newpath
= strdup(path
);
746 newpath
[len
- 1] = '\0';
747 ret
= lstat(newpath
, buf
);
750 ret
= lstat(path
, buf
);
757 /** Compute the MD5 message digest of a file.
758 * @param path file path of file to compute MD5 digest of
759 * @param output string to hold computed MD5 digest
760 * @return 0 on success, 1 on file open error, 2 on file read error
762 static int md5_file(const char *path
, unsigned char output
[16])
769 MALLOC(buf
, (size_t)ALPM_BUFFER_SIZE
, return 1);
771 OPEN(fd
, path
, O_RDONLY
);
779 while((n
= read(fd
, buf
, ALPM_BUFFER_SIZE
)) > 0 || errno
== EINTR
) {
783 MD5_Update(&ctx
, buf
, n
);
793 MD5_Final(output
, &ctx
);
797 /* third param is so we match the PolarSSL definition */
798 /** Compute the SHA-224 or SHA-256 message digest of a file.
799 * @param path file path of file to compute SHA2 digest of
800 * @param output string to hold computed SHA2 digest
801 * @param is224 use SHA-224 instead of SHA-256
802 * @return 0 on success, 1 on file open error, 2 on file read error
804 static int sha2_file(const char *path
, unsigned char output
[32], int is224
)
811 MALLOC(buf
, (size_t)ALPM_BUFFER_SIZE
, return 1);
813 OPEN(fd
, path
, O_RDONLY
);
825 while((n
= read(fd
, buf
, ALPM_BUFFER_SIZE
)) > 0 || errno
== EINTR
) {
830 SHA224_Update(&ctx
, buf
, n
);
832 SHA256_Update(&ctx
, buf
, n
);
844 SHA224_Final(output
, &ctx
);
846 SHA256_Final(output
, &ctx
);
852 /** Create a string representing bytes in hexadecimal.
853 * @param bytes the bytes to represent in hexadecimal
854 * @param size number of bytes to consider
855 * @return a NULL terminated string with the hexadecimal representation of
856 * bytes or NULL on error. This string must be freed.
858 static char *hex_representation(unsigned char *bytes
, size_t size
)
860 static const char *hex_digits
= "0123456789abcdef";
864 MALLOC(str
, 2 * size
+ 1, return NULL
);
866 for(i
= 0; i
< size
; i
++) {
867 str
[2 * i
] = hex_digits
[bytes
[i
] >> 4];
868 str
[2 * i
+ 1] = hex_digits
[bytes
[i
] & 0x0f];
871 str
[2 * size
] = '\0';
876 /** Get the md5 sum of file.
877 * @param filename name of the file
878 * @return the checksum on success, NULL on error
879 * @addtogroup alpm_misc
881 char SYMEXPORT
*alpm_compute_md5sum(const char *filename
)
883 unsigned char output
[16];
885 ASSERT(filename
!= NULL
, return NULL
);
887 /* defined above for OpenSSL, otherwise defined in md5.h */
888 if(md5_file(filename
, output
) > 0) {
892 return hex_representation(output
, 16);
895 /** Get the sha256 sum of file.
896 * @param filename name of the file
897 * @return the checksum on success, NULL on error
898 * @addtogroup alpm_misc
900 char SYMEXPORT
*alpm_compute_sha256sum(const char *filename
)
902 unsigned char output
[32];
904 ASSERT(filename
!= NULL
, return NULL
);
906 /* defined above for OpenSSL, otherwise defined in sha2.h */
907 if(sha2_file(filename
, output
, 0) > 0) {
911 return hex_representation(output
, 32);
914 /** Calculates a file's MD5 or SHA2 digest and compares it to an expected value.
915 * @param filepath path of the file to check
916 * @param expected hash value to compare against
917 * @param type digest type to use
918 * @return 0 if file matches the expected hash, 1 if they do not match, -1 on
921 int _alpm_test_checksum(const char *filepath
, const char *expected
,
922 alpm_pkgvalidation_t type
)
927 if(type
== ALPM_PKG_VALIDATION_MD5SUM
) {
928 computed
= alpm_compute_md5sum(filepath
);
929 } else if(type
== ALPM_PKG_VALIDATION_SHA256SUM
) {
930 computed
= alpm_compute_sha256sum(filepath
);
935 if(expected
== NULL
|| computed
== NULL
) {
937 } else if(strcmp(expected
, computed
) != 0) {
947 /* Note: does NOT handle sparse files on purpose for speed. */
949 * Does not handle sparse files on purpose for speed.
954 int _alpm_archive_fgets(struct archive
*a
, struct archive_read_buffer
*b
)
956 /* ensure we start populating our line buffer at the beginning */
957 b
->line_offset
= b
->line
;
960 size_t block_remaining
;
963 /* have we processed this entire block? */
964 if(b
->block
+ b
->block_size
== b
->block_offset
) {
966 if(b
->ret
== ARCHIVE_EOF
) {
967 /* reached end of archive on the last read, now we are out of data */
971 /* zero-copy - this is the entire next block of data. */
972 b
->ret
= archive_read_data_block(a
, (void *)&b
->block
,
973 &b
->block_size
, &offset
);
974 b
->block_offset
= b
->block
;
975 block_remaining
= b
->block_size
;
978 if(b
->ret
< ARCHIVE_OK
) {
982 block_remaining
= b
->block
+ b
->block_size
- b
->block_offset
;
985 /* look through the block looking for EOL characters */
986 eol
= memchr(b
->block_offset
, '\n', block_remaining
);
988 eol
= memchr(b
->block_offset
, '\0', block_remaining
);
991 /* allocate our buffer, or ensure our existing one is big enough */
993 /* set the initial buffer to the read block_size */
994 CALLOC(b
->line
, b
->block_size
+ 1, sizeof(char), b
->ret
= -ENOMEM
; goto cleanup
);
995 b
->line_size
= b
->block_size
+ 1;
996 b
->line_offset
= b
->line
;
998 /* note: we know eol > b->block_offset and b->line_offset > b->line,
999 * so we know the result is unsigned and can fit in size_t */
1000 size_t new = eol
? (size_t)(eol
- b
->block_offset
) : block_remaining
;
1001 size_t needed
= (size_t)((b
->line_offset
- b
->line
) + new + 1);
1002 if(needed
> b
->max_line_size
) {
1006 if(needed
> b
->line_size
) {
1007 /* need to realloc + copy data to fit total length */
1009 CALLOC(new_line
, needed
, sizeof(char), b
->ret
= -ENOMEM
; goto cleanup
);
1010 memcpy(new_line
, b
->line
, b
->line_size
);
1011 b
->line_size
= needed
;
1012 b
->line_offset
= new_line
+ (b
->line_offset
- b
->line
);
1019 size_t len
= (size_t)(eol
- b
->block_offset
);
1020 memcpy(b
->line_offset
, b
->block_offset
, len
);
1021 b
->line_offset
[len
] = '\0';
1022 b
->block_offset
= eol
+ 1;
1023 b
->real_line_size
= b
->line_offset
+ len
- b
->line
;
1024 /* this is the main return point; from here you can read b->line */
1027 /* we've looked through the whole block but no newline, copy it */
1028 size_t len
= (size_t)(b
->block
+ b
->block_size
- b
->block_offset
);
1029 memcpy(b
->line_offset
, b
->block_offset
, len
);
1030 b
->line_offset
+= len
;
1031 b
->block_offset
= b
->block
+ b
->block_size
;
1032 /* there was no new data, return what is left; saved ARCHIVE_EOF will be
1033 * returned on next call */
1035 b
->line_offset
[0] = '\0';
1036 b
->real_line_size
= b
->line_offset
- b
->line
;
1046 memset(b
, 0, sizeof(struct archive_read_buffer
));
1051 /** Parse a full package specifier.
1052 * @param target package specifier to parse, such as: "pacman-4.0.1-2",
1053 * "pacman-4.01-2/", or "pacman-4.0.1-2/desc"
1054 * @param name to hold package name
1055 * @param version to hold package version
1056 * @param name_hash to hold package name hash
1057 * @return 0 on success, -1 on error
1059 int _alpm_splitname(const char *target
, char **name
, char **version
,
1060 unsigned long *name_hash
)
1062 /* the format of a db entry is as follows:
1063 * package-version-rel/
1064 * package-version-rel/desc (we ignore the filename portion)
1065 * package name can contain hyphens, so parse from the back- go back
1066 * two hyphens and we have split the version from the name.
1068 const char *pkgver
, *end
;
1070 if(target
== NULL
) {
1074 /* remove anything trailing a '/' */
1075 end
= strchr(target
, '/');
1077 end
= target
+ strlen(target
);
1080 /* do the magic parsing- find the beginning of the version string
1081 * by doing two iterations of same loop to lop off two hyphens */
1082 for(pkgver
= end
- 1; *pkgver
&& *pkgver
!= '-'; pkgver
--);
1083 for(pkgver
= pkgver
- 1; *pkgver
&& *pkgver
!= '-'; pkgver
--);
1084 if(*pkgver
!= '-' || pkgver
== target
) {
1088 /* copy into fields and return */
1093 /* version actually points to the dash, so need to increment 1 and account
1094 * for potential end character */
1095 STRNDUP(*version
, pkgver
+ 1, end
- pkgver
- 1, return -1);
1102 STRNDUP(*name
, target
, pkgver
- target
, return -1);
1104 *name_hash
= _alpm_hash_sdbm(*name
);
1111 /** Hash the given string to an unsigned long value.
1112 * This is the standard sdbm hashing algorithm.
1113 * @param str string to hash
1114 * @return the hash value of the given string
1116 unsigned long _alpm_hash_sdbm(const char *str
)
1118 unsigned long hash
= 0;
1124 while((c
= *str
++)) {
1125 hash
= c
+ hash
* 65599;
1131 /** Convert a string to a file offset.
1132 * This parses bare positive integers only.
1133 * @param line string to convert
1134 * @return off_t on success, -1 on error
1136 off_t
_alpm_strtoofft(const char *line
)
1139 unsigned long long result
;
1142 /* we are trying to parse bare numbers only, no leading anything */
1143 if(!isdigit((unsigned char)line
[0])) {
1146 result
= strtoull(line
, &end
, 10);
1147 if(result
== 0 && end
== line
) {
1148 /* line was not a number */
1150 } else if(result
== ULLONG_MAX
&& errno
== ERANGE
) {
1151 /* line does not fit in unsigned long long */
1154 /* line began with a number but has junk left over at the end */
1158 return (off_t
)result
;
1161 /** Parses a date into an alpm_time_t struct.
1162 * @param line date to parse
1163 * @return time struct on success, 0 on error
1165 alpm_time_t
_alpm_parsedate(const char *line
)
1171 result
= strtoll(line
, &end
, 10);
1172 if(result
== 0 && end
== line
) {
1173 /* line was not a number */
1176 } else if(errno
== ERANGE
) {
1177 /* line does not fit in long long */
1180 /* line began with a number but has junk left over at the end */
1185 return (alpm_time_t
)result
;
1188 /** Wrapper around access() which takes a dir and file argument
1189 * separately and generates an appropriate error message.
1190 * If dir is NULL file will be treated as the whole path.
1191 * @param handle an alpm handle
1192 * @param dir directory path ending with and slash
1193 * @param file filename
1194 * @param amode access mode as described in access()
1195 * @return int value returned by access()
1197 int _alpm_access(alpm_handle_t
*handle
, const char *dir
, const char *file
, int amode
)
1205 len
= strlen(dir
) + strlen(file
) + 1;
1206 CALLOC(check_path
, len
, sizeof(char), RET_ERR(handle
, ALPM_ERR_MEMORY
, -1));
1207 snprintf(check_path
, len
, "%s%s", dir
, file
);
1209 ret
= access(check_path
, amode
);
1213 ret
= access(file
, amode
);
1218 _alpm_log(handle
, ALPM_LOG_DEBUG
, "\"%s%s\" is not readable: %s\n",
1219 dir
, file
, strerror(errno
));
1222 _alpm_log(handle
, ALPM_LOG_DEBUG
, "\"%s%s\" is not writable: %s\n",
1223 dir
, file
, strerror(errno
));
1226 _alpm_log(handle
, ALPM_LOG_DEBUG
, "\"%s%s\" is not executable: %s\n",
1227 dir
, file
, strerror(errno
));
1230 _alpm_log(handle
, ALPM_LOG_DEBUG
, "\"%s%s\" does not exist: %s\n",
1231 dir
, file
, strerror(errno
));
1237 /** Checks whether a string matches at least one shell wildcard pattern.
1238 * Checks for matches with fnmatch. Matches are inverted by prepending
1239 * patterns with an exclamation mark. Preceding exclamation marks may be
1240 * escaped. Subsequent matches override previous ones.
1241 * @param patterns patterns to match against
1242 * @param string string to check against pattern
1243 * @return 0 if string matches pattern, negative if they don't match and
1244 * positive if the last match was inverted
1246 int _alpm_fnmatch_patterns(alpm_list_t
*patterns
, const char *string
)
1252 for(i
= alpm_list_last(patterns
); i
; i
= alpm_list_previous(i
)) {
1255 inverted
= pattern
[0] == '!';
1256 if(inverted
|| pattern
[0] == '\\') {
1260 if(_alpm_fnmatch(pattern
, string
) == 0) {
1268 /** Checks whether a string matches a shell wildcard pattern.
1269 * Wrapper around fnmatch.
1270 * @param pattern pattern to match against
1271 * @param string string to check against pattern
1272 * @return 0 if string matches pattern, non-zero if they don't match and on
1275 int _alpm_fnmatch(const void *pattern
, const void *string
)
1277 return fnmatch(pattern
, string
, 0);
1280 void _alpm_alloc_fail(size_t size
)
1282 fprintf(stderr
, "alloc failure: could not allocate %zd bytes\n", size
);
1285 /* vim: set ts=2 sw=2 noet: */