Don't walk off front of string when stripping newline
[pacman-ng.git] / lib / libalpm / util.c
blobadb1d1925f801ed98a5c96e92ecc3c398ee9bbfe
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 #else
50 #include "md5.h"
51 #endif
53 /* libalpm */
54 #include "util.h"
55 #include "log.h"
56 #include "alpm.h"
57 #include "alpm_list.h"
58 #include "handle.h"
59 #include "trans.h"
61 #ifndef HAVE_STRSEP
62 /* This is a replacement for strsep which is not portable (missing on Solaris).
63 * Copyright (c) 2001 by François Gouget <fgouget_at_codeweavers.com> */
64 char* strsep(char** str, const char* delims)
66 char* token;
68 if(*str==NULL) {
69 /* No more tokens */
70 return NULL;
73 token=*str;
74 while(**str!='\0') {
75 if(strchr(delims,**str)!=NULL) {
76 **str='\0';
77 (*str)++;
78 return token;
80 (*str)++;
82 /* There is no other token */
83 *str=NULL;
84 return token;
86 #endif
88 int _alpm_makepath(const char *path)
90 return _alpm_makepath_mode(path, 0755);
93 /* does the same thing as 'mkdir -p' */
94 int _alpm_makepath_mode(const char *path, mode_t mode)
96 /* A bit of pointer hell here. Descriptions:
97 * orig - a copy of path so we can safely butcher it with strsep
98 * str - the current position in the path string (after the delimiter)
99 * ptr - the original position of str after calling strsep
100 * incr - incrementally generated path for use in stat/mkdir call
102 char *orig, *str, *ptr, *incr;
103 mode_t oldmask = umask(0000);
104 int ret = 0;
106 orig = strdup(path);
107 incr = calloc(strlen(orig) + 1, sizeof(char));
108 str = orig;
109 while((ptr = strsep(&str, "/"))) {
110 if(strlen(ptr)) {
111 /* we have another path component- append the newest component to
112 * existing string and create one more level of dir structure */
113 strcat(incr, "/");
114 strcat(incr, ptr);
115 if(access(incr, F_OK)) {
116 if(mkdir(incr, mode)) {
117 ret = 1;
118 break;
123 free(orig);
124 free(incr);
125 umask(oldmask);
126 return ret;
129 #define CPBUFSIZE 8 * 1024
131 int _alpm_copyfile(const char *src, const char *dest)
133 FILE *in, *out;
134 size_t len;
135 char *buf;
136 int ret = 0;
138 in = fopen(src, "rb");
139 if(in == NULL) {
140 return 1;
142 out = fopen(dest, "wb");
143 if(out == NULL) {
144 fclose(in);
145 return 1;
148 CALLOC(buf, (size_t)CPBUFSIZE, (size_t)1, ret = 1; goto cleanup;);
150 /* do the actual file copy */
151 while((len = fread(buf, 1, CPBUFSIZE, in))) {
152 size_t nwritten = 0;
153 nwritten = fwrite(buf, 1, len, out);
154 if((nwritten != len) || ferror(out)) {
155 ret = -1;
156 goto cleanup;
160 /* chmod dest to permissions of src, as long as it is not a symlink */
161 struct stat statbuf;
162 if(!stat(src, &statbuf)) {
163 if(! S_ISLNK(statbuf.st_mode)) {
164 fchmod(fileno(out), statbuf.st_mode);
166 } else {
167 /* stat was unsuccessful */
168 ret = 1;
171 cleanup:
172 fclose(in);
173 fclose(out);
174 FREE(buf);
175 return ret;
178 /* Trim whitespace and newlines from a string
180 char *_alpm_strtrim(char *str)
182 char *pch = str;
184 if(*str == '\0') {
185 /* string is empty, so we're done. */
186 return str;
189 while(isspace((unsigned char)*pch)) {
190 pch++;
192 if(pch != str) {
193 memmove(str, pch, (strlen(pch) + 1));
196 /* check if there wasn't anything but whitespace in the string. */
197 if(*str == '\0') {
198 return str;
201 pch = (str + (strlen(str) - 1));
202 while(isspace((unsigned char)*pch)) {
203 pch--;
205 *++pch = '\0';
207 return str;
211 * Trim trailing newline from a string (if one exists).
212 * @param str a single line of text
213 * @return the length of the trimmed string
215 size_t _alpm_strip_newline(char *str)
217 size_t len;
218 if(str == '\0') {
219 return 0;
221 len = strlen(str);
222 while(len > 0 && str[len - 1] == '\n') {
223 len--;
225 str[len] = '\0';
227 return len;
230 /* Compression functions */
233 * @brief Unpack a specific file in an archive.
235 * @param handle the context handle
236 * @param archive the archive to unpack
237 * @param prefix where to extract the files
238 * @param filename a file within the archive to unpack
239 * @return 0 on success, 1 on failure
241 int _alpm_unpack_single(alpm_handle_t *handle, const char *archive,
242 const char *prefix, const char *filename)
244 alpm_list_t *list = NULL;
245 int ret = 0;
246 if(filename == NULL) {
247 return 1;
249 list = alpm_list_add(list, (void *)filename);
250 ret = _alpm_unpack(handle, archive, prefix, list, 1);
251 alpm_list_free(list);
252 return ret;
256 * @brief Unpack a list of files in an archive.
258 * @param handle the context handle
259 * @param archive the archive to unpack
260 * @param prefix where to extract the files
261 * @param list a list of files within the archive to unpack or NULL for all
262 * @param breakfirst break after the first entry found
264 * @return 0 on success, 1 on failure
266 int _alpm_unpack(alpm_handle_t *handle, const char *archive, const char *prefix,
267 alpm_list_t *list, int breakfirst)
269 int ret = 0;
270 mode_t oldmask;
271 struct archive *_archive;
272 struct archive_entry *entry;
273 char cwd[PATH_MAX];
274 int restore_cwd = 0;
276 if((_archive = archive_read_new()) == NULL) {
277 RET_ERR(handle, ALPM_ERR_LIBARCHIVE, 1);
280 archive_read_support_compression_all(_archive);
281 archive_read_support_format_all(_archive);
283 if(archive_read_open_filename(_archive, archive,
284 ARCHIVE_DEFAULT_BYTES_PER_BLOCK) != ARCHIVE_OK) {
285 _alpm_log(handle, ALPM_LOG_ERROR, _("could not open file %s: %s\n"), archive,
286 archive_error_string(_archive));
287 RET_ERR(handle, ALPM_ERR_PKG_OPEN, 1);
290 oldmask = umask(0022);
292 /* save the cwd so we can restore it later */
293 if(getcwd(cwd, PATH_MAX) == NULL) {
294 _alpm_log(handle, ALPM_LOG_ERROR, _("could not get current working directory\n"));
295 } else {
296 restore_cwd = 1;
299 /* just in case our cwd was removed in the upgrade operation */
300 if(chdir(prefix) != 0) {
301 _alpm_log(handle, ALPM_LOG_ERROR, _("could not change directory to %s (%s)\n"),
302 prefix, strerror(errno));
303 ret = 1;
304 goto cleanup;
307 while(archive_read_next_header(_archive, &entry) == ARCHIVE_OK) {
308 const struct stat *st;
309 const char *entryname; /* the name of the file in the archive */
311 st = archive_entry_stat(entry);
312 entryname = archive_entry_pathname(entry);
314 if(S_ISREG(st->st_mode)) {
315 archive_entry_set_perm(entry, 0644);
316 } else if(S_ISDIR(st->st_mode)) {
317 archive_entry_set_perm(entry, 0755);
320 /* If specific files were requested, skip entries that don't match. */
321 if(list) {
322 char *entry_prefix = strdup(entryname);
323 char *p = strstr(entry_prefix,"/");
324 if(p) {
325 *(p+1) = '\0';
327 char *found = alpm_list_find_str(list, entry_prefix);
328 free(entry_prefix);
329 if(!found) {
330 if(archive_read_data_skip(_archive) != ARCHIVE_OK) {
331 ret = 1;
332 goto cleanup;
334 continue;
335 } else {
336 _alpm_log(handle, ALPM_LOG_DEBUG, "extracting: %s\n", entryname);
340 /* Extract the archive entry. */
341 int readret = archive_read_extract(_archive, entry, 0);
342 if(readret == ARCHIVE_WARN) {
343 /* operation succeeded but a non-critical error was encountered */
344 _alpm_log(handle, ALPM_LOG_WARNING, _("warning given when extracting %s (%s)\n"),
345 entryname, archive_error_string(_archive));
346 } else if(readret != ARCHIVE_OK) {
347 _alpm_log(handle, ALPM_LOG_ERROR, _("could not extract %s (%s)\n"),
348 entryname, archive_error_string(_archive));
349 ret = 1;
350 goto cleanup;
353 if(breakfirst) {
354 break;
358 cleanup:
359 umask(oldmask);
360 archive_read_finish(_archive);
361 if(restore_cwd && chdir(cwd) != 0) {
362 _alpm_log(handle, ALPM_LOG_ERROR, _("could not change directory to %s (%s)\n"),
363 cwd, strerror(errno));
365 return ret;
368 /* does the same thing as 'rm -rf' */
369 int _alpm_rmrf(const char *path)
371 int errflag = 0;
372 struct dirent *dp;
373 DIR *dirp;
374 char name[PATH_MAX];
375 struct stat st;
377 if(_alpm_lstat(path, &st) == 0) {
378 if(!S_ISDIR(st.st_mode)) {
379 if(!unlink(path)) {
380 return 0;
381 } else {
382 if(errno == ENOENT) {
383 return 0;
384 } else {
385 return 1;
388 } else {
389 dirp = opendir(path);
390 if(!dirp) {
391 return 1;
393 for(dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
394 if(dp->d_ino) {
395 sprintf(name, "%s/%s", path, dp->d_name);
396 if(strcmp(dp->d_name, "..") != 0 && strcmp(dp->d_name, ".") != 0) {
397 errflag += _alpm_rmrf(name);
401 closedir(dirp);
402 if(rmdir(path)) {
403 errflag++;
406 return errflag;
408 return 0;
412 * Determine if there are files in a directory
413 * @param handle the context handle
414 * @param path the full absolute directory path
415 * @param full_count whether to return an exact count of files
416 * @return a file count if full_count is != 0, else >0 if directory has
417 * contents, 0 if no contents, and -1 on error
419 ssize_t _alpm_files_in_directory(alpm_handle_t *handle, const char *path,
420 int full_count)
422 ssize_t files = 0;
423 struct dirent *ent;
424 DIR *dir = opendir(path);
426 if(!dir) {
427 if(errno == ENOTDIR) {
428 _alpm_log(handle, ALPM_LOG_DEBUG, "%s was not a directory\n", path);
429 } else {
430 _alpm_log(handle, ALPM_LOG_DEBUG, "could not read directory %s\n",
431 path);
433 return -1;
435 while((ent = readdir(dir)) != NULL) {
436 const char *name = ent->d_name;
438 if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
439 continue;
442 files++;
444 if(!full_count) {
445 break;
449 closedir(dir);
450 return files;
453 int _alpm_logaction(alpm_handle_t *handle, const char *fmt, va_list args)
455 int ret = 0;
457 if(handle->usesyslog) {
458 /* we can't use a va_list more than once, so we need to copy it
459 * so we can use the original when calling vfprintf below. */
460 va_list args_syslog;
461 va_copy(args_syslog, args);
462 vsyslog(LOG_WARNING, fmt, args_syslog);
463 va_end(args_syslog);
466 if(handle->logstream) {
467 time_t t;
468 struct tm *tm;
470 t = time(NULL);
471 tm = localtime(&t);
473 /* Use ISO-8601 date format */
474 fprintf(handle->logstream, "[%04d-%02d-%02d %02d:%02d] ",
475 tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
476 tm->tm_hour, tm->tm_min);
477 ret = vfprintf(handle->logstream, fmt, args);
478 fflush(handle->logstream);
481 return ret;
484 int _alpm_run_chroot(alpm_handle_t *handle, const char *path, char *const argv[])
486 char cwd[PATH_MAX];
487 pid_t pid;
488 int pipefd[2];
489 int restore_cwd = 0;
490 int retval = 0;
492 /* save the cwd so we can restore it later */
493 if(getcwd(cwd, PATH_MAX) == NULL) {
494 _alpm_log(handle, ALPM_LOG_ERROR, _("could not get current working directory\n"));
495 } else {
496 restore_cwd = 1;
499 /* just in case our cwd was removed in the upgrade operation */
500 if(chdir(handle->root) != 0) {
501 _alpm_log(handle, ALPM_LOG_ERROR, _("could not change directory to %s (%s)\n"),
502 handle->root, strerror(errno));
503 goto cleanup;
506 _alpm_log(handle, ALPM_LOG_DEBUG, "executing \"%s\" under chroot \"%s\"\n",
507 path, handle->root);
509 /* Flush open fds before fork() to avoid cloning buffers */
510 fflush(NULL);
512 if(pipe(pipefd) == -1) {
513 _alpm_log(handle, ALPM_LOG_ERROR, _("could not create pipe (%s)\n"), strerror(errno));
514 retval = 1;
515 goto cleanup;
518 /* fork- parent and child each have seperate code blocks below */
519 pid = fork();
520 if(pid == -1) {
521 _alpm_log(handle, ALPM_LOG_ERROR, _("could not fork a new process (%s)\n"), strerror(errno));
522 retval = 1;
523 goto cleanup;
526 if(pid == 0) {
527 /* this code runs for the child only (the actual chroot/exec) */
528 close(1);
529 close(2);
530 while(dup2(pipefd[1], 1) == -1 && errno == EINTR);
531 while(dup2(pipefd[1], 2) == -1 && errno == EINTR);
532 close(pipefd[0]);
533 close(pipefd[1]);
535 /* use fprintf instead of _alpm_log to send output through the parent */
536 if(chroot(handle->root) != 0) {
537 fprintf(stderr, _("could not change the root directory (%s)\n"), strerror(errno));
538 exit(1);
540 if(chdir("/") != 0) {
541 fprintf(stderr, _("could not change directory to %s (%s)\n"),
542 "/", strerror(errno));
543 exit(1);
545 umask(0022);
546 execv(path, argv);
547 fprintf(stderr, _("call to execv failed (%s)\n"), strerror(errno));
548 exit(1);
549 } else {
550 /* this code runs for the parent only (wait on the child) */
551 int status;
552 FILE *pipe_file;
554 close(pipefd[1]);
555 pipe_file = fdopen(pipefd[0], "r");
556 if(pipe_file == NULL) {
557 close(pipefd[0]);
558 retval = 1;
559 } else {
560 while(!feof(pipe_file)) {
561 char line[PATH_MAX];
562 if(fgets(line, PATH_MAX, pipe_file) == NULL)
563 break;
564 alpm_logaction(handle, "%s", line);
565 EVENT(handle->trans, ALPM_TRANS_EVT_SCRIPTLET_INFO, line, NULL);
567 fclose(pipe_file);
570 while(waitpid(pid, &status, 0) == -1) {
571 if(errno != EINTR) {
572 _alpm_log(handle, ALPM_LOG_ERROR, _("call to waitpid failed (%s)\n"), strerror(errno));
573 retval = 1;
574 goto cleanup;
578 /* report error from above after the child has exited */
579 if(retval != 0) {
580 _alpm_log(handle, ALPM_LOG_ERROR, _("could not open pipe (%s)\n"), strerror(errno));
581 goto cleanup;
583 /* check the return status, make sure it is 0 (success) */
584 if(WIFEXITED(status)) {
585 _alpm_log(handle, ALPM_LOG_DEBUG, "call to waitpid succeeded\n");
586 if(WEXITSTATUS(status) != 0) {
587 _alpm_log(handle, ALPM_LOG_ERROR, _("command failed to execute correctly\n"));
588 retval = 1;
593 cleanup:
594 if(restore_cwd && chdir(cwd) != 0) {
595 _alpm_log(handle, ALPM_LOG_ERROR, _("could not change directory to %s (%s)\n"), cwd, strerror(errno));
598 return retval;
601 int _alpm_ldconfig(alpm_handle_t *handle)
603 char line[PATH_MAX];
605 _alpm_log(handle, ALPM_LOG_DEBUG, "running ldconfig\n");
607 snprintf(line, PATH_MAX, "%setc/ld.so.conf", handle->root);
608 if(access(line, F_OK) == 0) {
609 snprintf(line, PATH_MAX, "%ssbin/ldconfig", handle->root);
610 if(access(line, X_OK) == 0) {
611 char *argv[] = { "ldconfig", NULL };
612 _alpm_run_chroot(handle, "/sbin/ldconfig", argv);
616 return 0;
619 /* Helper function for comparing strings using the
620 * alpm "compare func" signature */
621 int _alpm_str_cmp(const void *s1, const void *s2)
623 return strcmp(s1, s2);
626 /** Find a filename in a registered alpm cachedir.
627 * @param handle the context handle
628 * @param filename name of file to find
629 * @return malloced path of file, NULL if not found
631 char *_alpm_filecache_find(alpm_handle_t *handle, const char *filename)
633 char path[PATH_MAX];
634 char *retpath;
635 alpm_list_t *i;
636 struct stat buf;
638 /* Loop through the cache dirs until we find a matching file */
639 for(i = alpm_option_get_cachedirs(handle); i; i = alpm_list_next(i)) {
640 snprintf(path, PATH_MAX, "%s%s", (char *)alpm_list_getdata(i),
641 filename);
642 if(stat(path, &buf) == 0 && S_ISREG(buf.st_mode)) {
643 retpath = strdup(path);
644 _alpm_log(handle, ALPM_LOG_DEBUG, "found cached pkg: %s\n", retpath);
645 return retpath;
648 /* package wasn't found in any cachedir */
649 return NULL;
652 /** Check the alpm cachedirs for existance and find a writable one.
653 * If no valid cache directory can be found, use /tmp.
654 * @param handle the context handle
655 * @return pointer to a writable cache directory.
657 const char *_alpm_filecache_setup(alpm_handle_t *handle)
659 struct stat buf;
660 alpm_list_t *i, *tmp;
661 char *cachedir;
663 /* Loop through the cache dirs until we find a writeable dir */
664 for(i = alpm_option_get_cachedirs(handle); i; i = alpm_list_next(i)) {
665 cachedir = alpm_list_getdata(i);
666 if(stat(cachedir, &buf) != 0) {
667 /* cache directory does not exist.... try creating it */
668 _alpm_log(handle, ALPM_LOG_WARNING, _("no %s cache exists, creating...\n"),
669 cachedir);
670 if(_alpm_makepath(cachedir) == 0) {
671 _alpm_log(handle, ALPM_LOG_DEBUG, "using cachedir: %s\n", cachedir);
672 return cachedir;
674 } else if(S_ISDIR(buf.st_mode) && (buf.st_mode & S_IWUSR)) {
675 _alpm_log(handle, ALPM_LOG_DEBUG, "using cachedir: %s\n", cachedir);
676 return cachedir;
677 } else {
678 _alpm_log(handle, ALPM_LOG_DEBUG, "skipping cachedir: %s\n", cachedir);
682 /* we didn't find a valid cache directory. use /tmp. */
683 tmp = alpm_list_add(NULL, "/tmp/");
684 alpm_option_set_cachedirs(handle, tmp);
685 alpm_list_free(tmp);
686 _alpm_log(handle, ALPM_LOG_DEBUG, "using cachedir: %s\n", "/tmp/");
687 _alpm_log(handle, ALPM_LOG_WARNING, _("couldn't create package cache, using /tmp instead\n"));
688 return "/tmp/";
691 /** lstat wrapper that treats /path/dirsymlink/ the same as /path/dirsymlink.
692 * Linux lstat follows POSIX semantics and still performs a dereference on
693 * the first, and for uses of lstat in libalpm this is not what we want.
694 * @param path path to file to lstat
695 * @param buf structure to fill with stat information
696 * @return the return code from lstat
698 int _alpm_lstat(const char *path, struct stat *buf)
700 int ret;
701 size_t len = strlen(path);
703 /* strip the trailing slash if one exists */
704 if(len != 0 && path[len - 1] == '/') {
705 char *newpath = strdup(path);
706 newpath[len - 1] = '\0';
707 ret = lstat(newpath, buf);
708 free(newpath);
709 } else {
710 ret = lstat(path, buf);
713 return ret;
716 #ifdef HAVE_LIBSSL
717 static int md5_file(const char *path, unsigned char output[16])
719 FILE *f;
720 size_t n;
721 MD5_CTX ctx;
722 unsigned char *buf;
724 CALLOC(buf, 8192, sizeof(unsigned char), return 1);
726 if((f = fopen(path, "rb")) == NULL) {
727 free(buf);
728 return 1;
731 MD5_Init(&ctx);
733 while((n = fread(buf, 1, sizeof(buf), f)) > 0) {
734 MD5_Update(&ctx, buf, n);
737 MD5_Final(output, &ctx);
739 memset(&ctx, 0, sizeof(MD5_CTX));
740 free(buf);
742 if(ferror(f) != 0) {
743 fclose(f);
744 return 2;
747 fclose(f);
748 return 0;
750 #endif
752 /** Get the md5 sum of file.
753 * @param filename name of the file
754 * @return the checksum on success, NULL on error
755 * @addtogroup alpm_misc
757 char SYMEXPORT *alpm_compute_md5sum(const char *filename)
759 unsigned char output[16];
760 char *md5sum;
761 int ret, i;
763 ASSERT(filename != NULL, return NULL);
765 /* allocate 32 chars plus 1 for null */
766 CALLOC(md5sum, 33, sizeof(char), return NULL);
767 /* defined above for OpenSSL, otherwise defined in md5.h */
768 ret = md5_file(filename, output);
770 if(ret > 0) {
771 return NULL;
774 /* Convert the result to something readable */
775 for (i = 0; i < 16; i++) {
776 /* sprintf is acceptable here because we know our output */
777 sprintf(md5sum +(i * 2), "%02x", output[i]);
780 return md5sum;
783 int _alpm_test_md5sum(const char *filepath, const char *md5sum)
785 char *md5sum2;
786 int ret;
788 md5sum2 = alpm_compute_md5sum(filepath);
790 if(md5sum == NULL || md5sum2 == NULL) {
791 ret = -1;
792 } else if(strcmp(md5sum, md5sum2) != 0) {
793 ret = 1;
794 } else {
795 ret = 0;
798 FREE(md5sum2);
799 return ret;
802 /* Note: does NOT handle sparse files on purpose for speed. */
803 int _alpm_archive_fgets(struct archive *a, struct archive_read_buffer *b)
805 char *i = NULL;
806 int64_t offset;
807 int done = 0;
809 /* ensure we start populating our line buffer at the beginning */
810 b->line_offset = b->line;
812 while(1) {
813 /* have we processed this entire block? */
814 if(b->block + b->block_size == b->block_offset) {
815 if(b->ret == ARCHIVE_EOF) {
816 /* reached end of archive on the last read, now we are out of data */
817 goto cleanup;
820 /* zero-copy - this is the entire next block of data. */
821 b->ret = archive_read_data_block(a, (void *)&b->block,
822 &b->block_size, &offset);
823 b->block_offset = b->block;
825 /* error, cleanup */
826 if(b->ret < ARCHIVE_OK) {
827 goto cleanup;
831 /* loop through the block looking for EOL characters */
832 for(i = b->block_offset; i < (b->block + b->block_size); i++) {
833 /* check if read value was null or newline */
834 if(*i == '\0' || *i == '\n') {
835 done = 1;
836 break;
840 /* allocate our buffer, or ensure our existing one is big enough */
841 if(!b->line) {
842 /* set the initial buffer to the read block_size */
843 CALLOC(b->line, b->block_size + 1, sizeof(char), b->ret = -ENOMEM; goto cleanup);
844 b->line_size = b->block_size + 1;
845 b->line_offset = b->line;
846 } else {
847 size_t needed = (size_t)((b->line_offset - b->line)
848 + (i - b->block_offset) + 1);
849 if(needed > b->max_line_size) {
850 b->ret = -ERANGE;
851 goto cleanup;
853 if(needed > b->line_size) {
854 /* need to realloc + copy data to fit total length */
855 char *new;
856 CALLOC(new, needed, sizeof(char), b->ret = -ENOMEM; goto cleanup);
857 memcpy(new, b->line, b->line_size);
858 b->line_size = needed;
859 b->line_offset = new + (b->line_offset - b->line);
860 free(b->line);
861 b->line = new;
865 if(done) {
866 size_t len = (size_t)(i - b->block_offset);
867 memcpy(b->line_offset, b->block_offset, len);
868 b->line_offset[len] = '\0';
869 b->block_offset = ++i;
870 /* this is the main return point; from here you can read b->line */
871 return ARCHIVE_OK;
872 } else {
873 /* we've looked through the whole block but no newline, copy it */
874 size_t len = (size_t)(b->block + b->block_size - b->block_offset);
875 memcpy(b->line_offset, b->block_offset, len);
876 b->line_offset += len;
877 b->block_offset = i;
878 /* there was no new data, return what is left; saved ARCHIVE_EOF will be
879 * returned on next call */
880 if(len == 0) {
881 b->line_offset[0] = '\0';
882 return ARCHIVE_OK;
887 cleanup:
889 int ret = b->ret;
890 FREE(b->line);
891 memset(b, 0, sizeof(b));
892 return ret;
896 int _alpm_splitname(const char *target, char **name, char **version,
897 unsigned long *name_hash)
899 /* the format of a db entry is as follows:
900 * package-version-rel/
901 * package-version-rel/desc (we ignore the filename portion)
902 * package name can contain hyphens, so parse from the back- go back
903 * two hyphens and we have split the version from the name.
905 const char *pkgver, *end;
907 if(target == NULL) {
908 return -1;
911 /* remove anything trailing a '/' */
912 end = strchr(target, '/');
913 if(!end) {
914 end = target + strlen(target);
917 /* do the magic parsing- find the beginning of the version string
918 * by doing two iterations of same loop to lop off two hyphens */
919 for(pkgver = end - 1; *pkgver && *pkgver != '-'; pkgver--);
920 for(pkgver = pkgver - 1; *pkgver && *pkgver != '-'; pkgver--);
921 if(*pkgver != '-' || pkgver == target) {
922 return -1;
925 /* copy into fields and return */
926 if(version) {
927 if(*version) {
928 FREE(*version);
930 /* version actually points to the dash, so need to increment 1 and account
931 * for potential end character */
932 STRNDUP(*version, pkgver + 1, end - pkgver - 1, return -1);
935 if(name) {
936 if(*name) {
937 FREE(*name);
939 STRNDUP(*name, target, pkgver - target, return -1);
940 if(name_hash) {
941 *name_hash = _alpm_hash_sdbm(*name);
945 return 0;
949 * Hash the given string to an unsigned long value.
950 * This is the standard sdbm hashing algorithm.
951 * @param str string to hash
952 * @return the hash value of the given string
954 unsigned long _alpm_hash_sdbm(const char *str)
956 unsigned long hash = 0;
957 int c;
959 if(!str) {
960 return hash;
962 while((c = *str++)) {
963 hash = c + (hash << 6) + (hash << 16) - hash;
966 return hash;
969 long _alpm_parsedate(const char *line)
971 if(isalpha((unsigned char)line[0])) {
972 /* initialize to null in case of failure */
973 struct tm tmp_tm = { 0 };
974 setlocale(LC_TIME, "C");
975 strptime(line, "%a %b %e %H:%M:%S %Y", &tmp_tm);
976 setlocale(LC_TIME, "");
977 return mktime(&tmp_tm);
979 return atol(line);
983 * Wrapper around access() which takes a dir and file argument
984 * separately and generates an appropriate error message.
985 * If dir is NULL file will be treated as the whole path.
986 * @param handle an alpm handle
987 * @param dir directory path ending with and slash
988 * @param file filename
989 * @param amode access mode as described in access()
990 * @return int value returned by access()
993 int _alpm_access(alpm_handle_t *handle, const char *dir, const char *file, int amode)
995 size_t len = 0;
996 int ret = 0;
998 if (dir) {
999 char *check_path;
1001 len = strlen(dir) + strlen(file) + 1;
1002 CALLOC(check_path, len, sizeof(char), RET_ERR(handle, ALPM_ERR_MEMORY, -1));
1003 snprintf(check_path, len, "%s%s", dir, file);
1005 ret = access(check_path, amode);
1006 free(check_path);
1007 } else {
1008 dir = "";
1009 ret = access(file, amode);
1012 if(ret != 0) {
1013 if (amode & R_OK) {
1014 _alpm_log(handle, ALPM_LOG_DEBUG, "\"%s%s\" is not readable: %s\n",
1015 dir, file, strerror(errno));
1017 if (amode & W_OK) {
1018 _alpm_log(handle, ALPM_LOG_DEBUG, "\"%s%s\" is not writable: %s\n",
1019 dir, file, strerror(errno));
1021 if (amode & X_OK) {
1022 _alpm_log(handle, ALPM_LOG_DEBUG, "\"%s%s\" is not executable: %s\n",
1023 dir, file, strerror(errno));
1025 if (amode == F_OK) {
1026 _alpm_log(handle, ALPM_LOG_DEBUG, "\"%s%s\" does not exist: %s\n",
1027 dir, file, strerror(errno));
1030 return ret;
1033 #ifndef HAVE_STRNDUP
1034 /* A quick and dirty implementation derived from glibc */
1035 static size_t strnlen(const char *s, size_t max)
1037 register const char *p;
1038 for(p = s; *p && max--; ++p);
1039 return (p - s);
1042 char *strndup(const char *s, size_t n)
1044 size_t len = strnlen(s, n);
1045 char *new = (char *) malloc(len + 1);
1047 if(new == NULL)
1048 return NULL;
1050 new[len] = '\0';
1051 return (char *)memcpy(new, s, len);
1053 #endif
1055 /* vim: set ts=2 sw=2 noet: */