Fix function indentation
[pacman-ng.git] / lib / libalpm / util.c
blobe526079765d742093cf69423956312e7d290cfea
1 /*
2 * util.c
4 * Copyright (c) 2006-2010 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 <fcntl.h>
32 #include <unistd.h>
33 #include <ctype.h>
34 #include <dirent.h>
35 #include <time.h>
36 #include <syslog.h>
37 #include <errno.h>
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <sys/wait.h>
42 /* libarchive */
43 #include <archive.h>
44 #include <archive_entry.h>
46 #ifdef HAVE_LIBSSL
47 #include <openssl/md5.h>
48 #else
49 #include "md5.h"
50 #endif
52 /* libalpm */
53 #include "util.h"
54 #include "log.h"
55 #include "package.h"
56 #include "alpm.h"
57 #include "alpm_list.h"
58 #include "handle.h"
60 #ifndef HAVE_STRSEP
61 /* This is a replacement for strsep which is not portable (missing on Solaris).
62 * Copyright (c) 2001 by François Gouget <fgouget_at_codeweavers.com> */
63 char* strsep(char** str, const char* delims)
65 char* token;
67 if (*str==NULL) {
68 /* No more tokens */
69 return NULL;
72 token=*str;
73 while (**str!='\0') {
74 if (strchr(delims,**str)!=NULL) {
75 **str='\0';
76 (*str)++;
77 return token;
79 (*str)++;
81 /* There is no other token */
82 *str=NULL;
83 return token;
85 #endif
87 int _alpm_makepath(const char *path)
89 return(_alpm_makepath_mode(path, 0755));
92 /* does the same thing as 'mkdir -p' */
93 int _alpm_makepath_mode(const char *path, mode_t mode)
95 /* A bit of pointer hell here. Descriptions:
96 * orig - a copy of path so we can safely butcher it with strsep
97 * str - the current position in the path string (after the delimiter)
98 * ptr - the original position of str after calling strsep
99 * incr - incrementally generated path for use in stat/mkdir call
101 char *orig, *str, *ptr, *incr;
102 mode_t oldmask = umask(0000);
103 int ret = 0;
105 orig = strdup(path);
106 incr = calloc(strlen(orig) + 1, sizeof(char));
107 str = orig;
108 while((ptr = strsep(&str, "/"))) {
109 if(strlen(ptr)) {
110 /* we have another path component- append the newest component to
111 * existing string and create one more level of dir structure */
112 strcat(incr, "/");
113 strcat(incr, ptr);
114 if(access(incr, F_OK)) {
115 if(mkdir(incr, mode)) {
116 ret = 1;
117 break;
122 free(orig);
123 free(incr);
124 umask(oldmask);
125 return(ret);
128 #define CPBUFSIZE 8 * 1024
130 int _alpm_copyfile(const char *src, const char *dest)
132 FILE *in, *out;
133 size_t len;
134 char *buf;
135 int ret = 0;
137 in = fopen(src, "rb");
138 if(in == NULL) {
139 return(1);
141 out = fopen(dest, "wb");
142 if(out == NULL) {
143 fclose(in);
144 return(1);
147 CALLOC(buf, (size_t)CPBUFSIZE, (size_t)1, ret = 1; goto cleanup;);
149 /* do the actual file copy */
150 while((len = fread(buf, 1, CPBUFSIZE, in))) {
151 size_t nwritten = 0;
152 nwritten = fwrite(buf, 1, len, out);
153 if((nwritten != len) || ferror(out)) {
154 pm_errno = PM_ERR_WRITE;
155 _alpm_log(PM_LOG_ERROR, _("error writing to file '%s': %s\n"),
156 dest, strerror(errno));
157 ret = -1;
158 goto cleanup;
162 /* chmod dest to permissions of src, as long as it is not a symlink */
163 struct stat statbuf;
164 if(!stat(src, &statbuf)) {
165 if(! S_ISLNK(statbuf.st_mode)) {
166 fchmod(fileno(out), statbuf.st_mode);
168 } else {
169 /* stat was unsuccessful */
170 ret = 1;
173 cleanup:
174 fclose(in);
175 fclose(out);
176 FREE(buf);
177 return(ret);
180 /* Trim whitespace and newlines from a string
182 char *_alpm_strtrim(char *str)
184 char *pch = str;
186 if(*str == '\0') {
187 /* string is empty, so we're done. */
188 return(str);
191 while(isspace((unsigned char)*pch)) {
192 pch++;
194 if(pch != str) {
195 memmove(str, pch, (strlen(pch) + 1));
198 /* check if there wasn't anything but whitespace in the string. */
199 if(*str == '\0') {
200 return(str);
203 pch = (str + (strlen(str) - 1));
204 while(isspace((unsigned char)*pch)) {
205 pch--;
207 *++pch = '\0';
209 return(str);
212 /* Create a lock file */
213 int _alpm_lckmk()
215 int fd;
216 char *dir, *ptr;
217 const char *file = alpm_option_get_lockfile();
219 /* create the dir of the lockfile first */
220 dir = strdup(file);
221 ptr = strrchr(dir, '/');
222 if(ptr) {
223 *ptr = '\0';
225 _alpm_makepath(dir);
226 FREE(dir);
228 do {
229 fd = open(file, O_WRONLY | O_CREAT | O_EXCL, 0000);
230 } while (fd == -1 && errno == EINTR);
231 if(fd > 0) {
232 FILE *f = fdopen(fd, "w");
233 fprintf(f, "%ld\n", (long)getpid());
234 fflush(f);
235 fsync(fd);
236 fclose(f);
237 return(fd);
239 return(-1);
242 /* Remove a lock file */
243 int _alpm_lckrm()
245 const char *file = alpm_option_get_lockfile();
246 if(unlink(file) == -1 && errno != ENOENT) {
247 return(-1);
249 return(0);
252 /* Compression functions */
255 * @brief Unpack a specific file in an archive.
257 * @param archive the archive to unpack
258 * @param prefix where to extract the files
259 * @param fn a file within the archive to unpack
260 * @return 0 on success, 1 on failure
262 int _alpm_unpack_single(const char *archive, const char *prefix, const char *fn)
264 alpm_list_t *list = NULL;
265 int ret = 0;
266 if(fn == NULL) {
267 return(1);
269 list = alpm_list_add(list, (void *)fn);
270 ret = _alpm_unpack(archive, prefix, list, 1);
271 alpm_list_free(list);
272 return(ret);
276 * @brief Unpack a list of files in an archive.
278 * @param archive the archive to unpack
279 * @param prefix where to extract the files
280 * @param list a list of files within the archive to unpack or
281 * NULL for all
282 * @param breakfirst break after the first entry found
284 * @return 0 on success, 1 on failure
286 int _alpm_unpack(const char *archive, const char *prefix, alpm_list_t *list, int breakfirst)
288 int ret = 0;
289 mode_t oldmask;
290 struct archive *_archive;
291 struct archive_entry *entry;
292 char cwd[PATH_MAX];
293 int restore_cwd = 0;
295 ALPM_LOG_FUNC;
297 if((_archive = archive_read_new()) == NULL)
298 RET_ERR(PM_ERR_LIBARCHIVE, 1);
300 archive_read_support_compression_all(_archive);
301 archive_read_support_format_all(_archive);
303 if(archive_read_open_filename(_archive, archive,
304 ARCHIVE_DEFAULT_BYTES_PER_BLOCK) != ARCHIVE_OK) {
305 _alpm_log(PM_LOG_ERROR, _("could not open %s: %s\n"), archive,
306 archive_error_string(_archive));
307 RET_ERR(PM_ERR_PKG_OPEN, 1);
310 oldmask = umask(0022);
312 /* save the cwd so we can restore it later */
313 if(getcwd(cwd, PATH_MAX) == NULL) {
314 _alpm_log(PM_LOG_ERROR, _("could not get current working directory\n"));
315 } else {
316 restore_cwd = 1;
319 /* just in case our cwd was removed in the upgrade operation */
320 if(chdir(prefix) != 0) {
321 _alpm_log(PM_LOG_ERROR, _("could not change directory to %s (%s)\n"), prefix, strerror(errno));
322 ret = 1;
323 goto cleanup;
326 while(archive_read_next_header(_archive, &entry) == ARCHIVE_OK) {
327 const struct stat *st;
328 const char *entryname; /* the name of the file in the archive */
330 st = archive_entry_stat(entry);
331 entryname = archive_entry_pathname(entry);
333 if(S_ISREG(st->st_mode)) {
334 archive_entry_set_perm(entry, 0644);
335 } else if(S_ISDIR(st->st_mode)) {
336 archive_entry_set_perm(entry, 0755);
339 /* If specific files were requested, skip entries that don't match. */
340 if(list) {
341 char *prefix = strdup(entryname);
342 char *p = strstr(prefix,"/");
343 if(p) {
344 *(p+1) = '\0';
346 char *found = alpm_list_find_str(list, prefix);
347 free(prefix);
348 if(!found) {
349 if (archive_read_data_skip(_archive) != ARCHIVE_OK) {
350 ret = 1;
351 goto cleanup;
353 continue;
354 } else {
355 _alpm_log(PM_LOG_DEBUG, "extracting: %s\n", entryname);
359 /* Extract the archive entry. */
360 int readret = archive_read_extract(_archive, entry, 0);
361 if(readret == ARCHIVE_WARN) {
362 /* operation succeeded but a non-critical error was encountered */
363 _alpm_log(PM_LOG_WARNING, _("warning given while extracting %s (%s)\n"),
364 entryname, archive_error_string(_archive));
365 } else if(readret != ARCHIVE_OK) {
366 _alpm_log(PM_LOG_ERROR, _("could not extract %s (%s)\n"),
367 entryname, archive_error_string(_archive));
368 ret = 1;
369 goto cleanup;
372 if(breakfirst) {
373 break;
377 cleanup:
378 umask(oldmask);
379 archive_read_finish(_archive);
380 if(restore_cwd && chdir(cwd) != 0) {
381 _alpm_log(PM_LOG_ERROR, _("could not change directory to %s (%s)\n"), cwd, strerror(errno));
383 return(ret);
386 /* does the same thing as 'rm -rf' */
387 int _alpm_rmrf(const char *path)
389 int errflag = 0;
390 struct dirent *dp;
391 DIR *dirp;
392 char name[PATH_MAX];
393 struct stat st;
395 if(_alpm_lstat(path, &st) == 0) {
396 if(!S_ISDIR(st.st_mode)) {
397 if(!unlink(path)) {
398 return(0);
399 } else {
400 if(errno == ENOENT) {
401 return(0);
402 } else {
403 return(1);
406 } else {
407 dirp = opendir(path);
408 if(!dirp) {
409 return(1);
411 for(dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
412 if(dp->d_ino) {
413 sprintf(name, "%s/%s", path, dp->d_name);
414 if(strcmp(dp->d_name, "..") != 0 && strcmp(dp->d_name, ".") != 0) {
415 errflag += _alpm_rmrf(name);
419 closedir(dirp);
420 if(rmdir(path)) {
421 errflag++;
424 return(errflag);
426 return(0);
429 int _alpm_logaction(int usesyslog, FILE *f, const char *fmt, va_list args)
431 int ret = 0;
433 if(usesyslog) {
434 /* we can't use a va_list more than once, so we need to copy it
435 * so we can use the original when calling vfprintf below. */
436 va_list args_syslog;
437 va_copy(args_syslog, args);
438 vsyslog(LOG_WARNING, fmt, args_syslog);
439 va_end(args_syslog);
442 if(f) {
443 time_t t;
444 struct tm *tm;
446 t = time(NULL);
447 tm = localtime(&t);
449 /* Use ISO-8601 date format */
450 fprintf(f, "[%04d-%02d-%02d %02d:%02d] ",
451 tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
452 tm->tm_hour, tm->tm_min);
453 ret = vfprintf(f, fmt, args);
454 fflush(f);
457 return(ret);
460 int _alpm_run_chroot(const char *root, const char *path, char *const argv[])
462 char cwd[PATH_MAX];
463 pid_t pid;
464 int pipefd[2];
465 int restore_cwd = 0;
466 int retval = 0;
468 ALPM_LOG_FUNC;
470 /* save the cwd so we can restore it later */
471 if(getcwd(cwd, PATH_MAX) == NULL) {
472 _alpm_log(PM_LOG_ERROR, _("could not get current working directory\n"));
473 } else {
474 restore_cwd = 1;
477 /* just in case our cwd was removed in the upgrade operation */
478 if(chdir(root) != 0) {
479 _alpm_log(PM_LOG_ERROR, _("could not change directory to %s (%s)\n"), root, strerror(errno));
480 goto cleanup;
483 _alpm_log(PM_LOG_DEBUG, "executing \"%s\" under chroot \"%s\"\n", path, root);
485 /* Flush open fds before fork() to avoid cloning buffers */
486 fflush(NULL);
488 if(pipe(pipefd) == -1) {
489 _alpm_log(PM_LOG_ERROR, _("could not create pipe (%s)\n"), strerror(errno));
490 retval = 1;
491 goto cleanup;
494 /* fork- parent and child each have seperate code blocks below */
495 pid = fork();
496 if(pid == -1) {
497 _alpm_log(PM_LOG_ERROR, _("could not fork a new process (%s)\n"), strerror(errno));
498 retval = 1;
499 goto cleanup;
502 if(pid == 0) {
503 /* this code runs for the child only (the actual chroot/exec) */
504 close(1);
505 close(2);
506 while(dup2(pipefd[1], 1) == -1 && errno == EINTR);
507 while(dup2(pipefd[1], 2) == -1 && errno == EINTR);
508 close(pipefd[0]);
509 close(pipefd[1]);
511 /* use fprintf instead of _alpm_log to send output through the parent */
512 if(chroot(root) != 0) {
513 fprintf(stderr, _("could not change the root directory (%s)\n"), strerror(errno));
514 exit(1);
516 if(chdir("/") != 0) {
517 fprintf(stderr, _("could not change directory to / (%s)\n"), strerror(errno));
518 exit(1);
520 umask(0022);
521 execv(path, argv);
522 fprintf(stderr, _("call to execv failed (%s)\n"), strerror(errno));
523 exit(1);
524 } else {
525 /* this code runs for the parent only (wait on the child) */
526 int status;
527 FILE *pipe;
529 close(pipefd[1]);
530 pipe = fdopen(pipefd[0], "r");
531 if(pipe == NULL) {
532 close(pipefd[0]);
533 retval = 1;
534 } else {
535 while(!feof(pipe)) {
536 char line[PATH_MAX];
537 if(fgets(line, PATH_MAX, pipe) == NULL)
538 break;
539 alpm_logaction("%s", line);
540 EVENT(handle->trans, PM_TRANS_EVT_SCRIPTLET_INFO, line, NULL);
542 fclose(pipe);
545 while(waitpid(pid, &status, 0) == -1) {
546 if(errno != EINTR) {
547 _alpm_log(PM_LOG_ERROR, _("call to waitpid failed (%s)\n"), strerror(errno));
548 retval = 1;
549 goto cleanup;
553 /* report error from above after the child has exited */
554 if(retval != 0) {
555 _alpm_log(PM_LOG_ERROR, _("could not open pipe (%s)\n"), strerror(errno));
556 goto cleanup;
558 /* check the return status, make sure it is 0 (success) */
559 if(WIFEXITED(status)) {
560 _alpm_log(PM_LOG_DEBUG, "call to waitpid succeeded\n");
561 if(WEXITSTATUS(status) != 0) {
562 _alpm_log(PM_LOG_ERROR, _("command failed to execute correctly\n"));
563 retval = 1;
568 cleanup:
569 if(restore_cwd && chdir(cwd) != 0) {
570 _alpm_log(PM_LOG_ERROR, _("could not change directory to %s (%s)\n"), cwd, strerror(errno));
573 return(retval);
576 int _alpm_ldconfig(const char *root)
578 char line[PATH_MAX];
580 _alpm_log(PM_LOG_DEBUG, "running ldconfig\n");
582 snprintf(line, PATH_MAX, "%setc/ld.so.conf", root);
583 if(access(line, F_OK) == 0) {
584 snprintf(line, PATH_MAX, "%ssbin/ldconfig", root);
585 if(access(line, X_OK) == 0) {
586 char *argv[] = { "ldconfig", NULL };
587 _alpm_run_chroot(root, "/sbin/ldconfig", argv);
591 return(0);
594 /* Helper function for comparing strings using the
595 * alpm "compare func" signature */
596 int _alpm_str_cmp(const void *s1, const void *s2)
598 return(strcmp(s1, s2));
601 /** Find a filename in a registered alpm cachedir.
602 * @param filename name of file to find
603 * @return malloced path of file, NULL if not found
605 char *_alpm_filecache_find(const char* filename)
607 char path[PATH_MAX];
608 char *retpath;
609 alpm_list_t *i;
611 /* Loop through the cache dirs until we find a matching file */
612 for(i = alpm_option_get_cachedirs(); i; i = alpm_list_next(i)) {
613 snprintf(path, PATH_MAX, "%s%s", (char*)alpm_list_getdata(i),
614 filename);
615 if(access(path, R_OK) == 0) {
616 retpath = strdup(path);
617 _alpm_log(PM_LOG_DEBUG, "found cached pkg: %s\n", retpath);
618 return(retpath);
621 /* package wasn't found in any cachedir */
622 return(NULL);
625 /** Check the alpm cachedirs for existance and find a writable one.
626 * If no valid cache directory can be found, use /tmp.
627 * @return pointer to a writable cache directory.
629 const char *_alpm_filecache_setup(void)
631 struct stat buf;
632 alpm_list_t *i, *tmp;
633 char *cachedir;
635 /* Loop through the cache dirs until we find a writeable dir */
636 for(i = alpm_option_get_cachedirs(); i; i = alpm_list_next(i)) {
637 cachedir = alpm_list_getdata(i);
638 if(stat(cachedir, &buf) != 0) {
639 /* cache directory does not exist.... try creating it */
640 _alpm_log(PM_LOG_WARNING, _("no %s cache exists, creating...\n"),
641 cachedir);
642 if(_alpm_makepath(cachedir) == 0) {
643 _alpm_log(PM_LOG_DEBUG, "using cachedir: %s\n", cachedir);
644 return(cachedir);
646 } else if(S_ISDIR(buf.st_mode) && (buf.st_mode & S_IWUSR)) {
647 _alpm_log(PM_LOG_DEBUG, "using cachedir: %s\n", cachedir);
648 return(cachedir);
652 /* we didn't find a valid cache directory. use /tmp. */
653 tmp = alpm_list_add(NULL, strdup("/tmp/"));
654 alpm_option_set_cachedirs(tmp);
655 _alpm_log(PM_LOG_DEBUG, "using cachedir: %s", "/tmp/\n");
656 _alpm_log(PM_LOG_WARNING, _("couldn't create package cache, using /tmp instead\n"));
657 return(alpm_list_getdata(tmp));
660 /** lstat wrapper that treats /path/dirsymlink/ the same as /path/dirsymlink.
661 * Linux lstat follows POSIX semantics and still performs a dereference on
662 * the first, and for uses of lstat in libalpm this is not what we want.
663 * @param path path to file to lstat
664 * @param buf structure to fill with stat information
665 * @return the return code from lstat
667 int _alpm_lstat(const char *path, struct stat *buf)
669 int ret;
670 char *newpath = strdup(path);
671 int len = strlen(newpath);
673 /* strip the trailing slash if one exists */
674 if(len != 0 && newpath[len - 1] == '/') {
675 newpath[len - 1] = '\0';
678 ret = lstat(newpath, buf);
680 FREE(newpath);
681 return(ret);
684 #ifdef HAVE_LIBSSL
685 static int md5_file(const char *path, unsigned char output[16])
687 FILE *f;
688 size_t n;
689 MD5_CTX ctx;
690 unsigned char *buf;
692 CALLOC(buf, 8192, sizeof(unsigned char), return(1));
694 if((f = fopen(path, "rb")) == NULL) {
695 free(buf);
696 return(1);
699 MD5_Init(&ctx);
701 while((n = fread(buf, 1, sizeof(buf), f)) > 0) {
702 MD5_Update(&ctx, buf, n);
705 MD5_Final(output, &ctx);
707 memset(&ctx, 0, sizeof(MD5_CTX));
708 free(buf);
710 if(ferror(f) != 0) {
711 fclose(f);
712 return(2);
715 fclose(f);
716 return(0);
718 #endif
720 /** Get the md5 sum of file.
721 * @param filename name of the file
722 * @return the checksum on success, NULL on error
723 * @addtogroup alpm_misc
725 char SYMEXPORT *alpm_compute_md5sum(const char *filename)
727 unsigned char output[16];
728 char *md5sum;
729 int ret, i;
731 ALPM_LOG_FUNC;
733 ASSERT(filename != NULL, return(NULL));
735 /* allocate 32 chars plus 1 for null */
736 md5sum = calloc(33, sizeof(char));
737 /* defined above for OpenSSL, otherwise defined in md5.h */
738 ret = md5_file(filename, output);
740 if (ret > 0) {
741 RET_ERR(PM_ERR_NOT_A_FILE, NULL);
744 /* Convert the result to something readable */
745 for (i = 0; i < 16; i++) {
746 /* sprintf is acceptable here because we know our output */
747 sprintf(md5sum +(i * 2), "%02x", output[i]);
749 md5sum[32] = '\0';
751 _alpm_log(PM_LOG_DEBUG, "md5(%s) = %s\n", filename, md5sum);
752 return(md5sum);
755 int _alpm_test_md5sum(const char *filepath, const char *md5sum)
757 char *md5sum2;
758 int ret;
760 md5sum2 = alpm_compute_md5sum(filepath);
762 if(md5sum == NULL || md5sum2 == NULL) {
763 ret = -1;
764 } else if(strcmp(md5sum, md5sum2) != 0) {
765 ret = 1;
766 } else {
767 ret = 0;
770 FREE(md5sum2);
771 return(ret);
774 /* Note: does NOT handle sparse files on purpose for speed. */
775 int _alpm_archive_fgets(struct archive *a, struct archive_read_buffer *b)
777 char *i = NULL;
778 int64_t offset;
779 int done = 0;
781 while(1) {
782 /* have we processed this entire block? */
783 if(b->block + b->block_size == b->block_offset) {
784 if(b->ret == ARCHIVE_EOF) {
785 /* reached end of archive on the last read, now we are out of data */
786 goto cleanup;
789 /* zero-copy - this is the entire next block of data. */
790 b->ret = archive_read_data_block(a, (void*)&b->block,
791 &b->block_size, &offset);
792 b->block_offset = b->block;
794 /* error or end of archive with no data read, cleanup */
795 if(b->ret < ARCHIVE_OK ||
796 (b->block_size == 0 && b->ret == ARCHIVE_EOF)) {
797 goto cleanup;
801 /* loop through the block looking for EOL characters */
802 for(i = b->block_offset; i < (b->block + b->block_size); i++) {
803 /* check if read value was null or newline */
804 if(*i == '\0' || *i == '\n') {
805 done = 1;
806 break;
810 /* allocate our buffer, or ensure our existing one is big enough */
811 if(!b->line) {
812 /* set the initial buffer to the read block_size */
813 CALLOC(b->line, b->block_size + 1, sizeof(char),
814 RET_ERR(PM_ERR_MEMORY, -1));
815 b->line_size = b->block_size + 1;
816 b->line_offset = b->line;
817 } else {
818 size_t needed = (b->line_offset - b->line) + (i - b->block_offset) + 1;
819 if(needed > b->max_line_size) {
820 RET_ERR(PM_ERR_MEMORY, -1);
822 if(needed > b->line_size) {
823 /* need to realloc + copy data to fit total length */
824 char *new;
825 CALLOC(new, needed, sizeof(char), RET_ERR(PM_ERR_MEMORY, -1));
826 memcpy(new, b->line, b->line_size);
827 b->line_size = needed;
828 b->line_offset = new + (b->line_offset - b->line);
829 free(b->line);
830 b->line = new;
834 if(done) {
835 size_t len = i - b->block_offset;
836 memcpy(b->line_offset, b->block_offset, len);
837 b->line_offset[len] = '\0';
838 b->block_offset = ++i;
839 /* this is the main return point; from here you can read b->line */
840 return(ARCHIVE_OK);
841 } else {
842 /* we've looked through the whole block but no newline, copy it */
843 size_t len = b->block + b->block_size - b->block_offset;
844 memcpy(b->line_offset, b->block_offset, len);
845 b->line_offset += len;
846 b->block_offset = i;
850 cleanup:
852 int ret = b->ret;
853 FREE(b->line);
854 memset(b, 0, sizeof(b));
855 return(ret);
859 int _alpm_splitname(const char *target, pmpkg_t *pkg)
861 /* the format of a db entry is as follows:
862 * package-version-rel/
863 * package name can contain hyphens, so parse from the back- go back
864 * two hyphens and we have split the version from the name.
866 char *tmp, *p, *q;
868 if(target == NULL || pkg == NULL) {
869 return(-1);
871 STRDUP(tmp, target, RET_ERR(PM_ERR_MEMORY, -1));
872 p = tmp + strlen(tmp);
874 /* remove any trailing '/' */
875 while (*(p - 1) == '/') {
876 --p;
877 *p = '\0';
880 /* do the magic parsing- find the beginning of the version string
881 * by doing two iterations of same loop to lop off two hyphens */
882 for(q = --p; *q && *q != '-'; q--);
883 for(p = --q; *p && *p != '-'; p--);
884 if(*p != '-' || p == tmp) {
885 return(-1);
888 /* copy into fields and return */
889 if(pkg->version) {
890 FREE(pkg->version);
892 STRDUP(pkg->version, p+1, RET_ERR(PM_ERR_MEMORY, -1));
893 /* insert a terminator at the end of the name (on hyphen)- then copy it */
894 *p = '\0';
895 if(pkg->name) {
896 FREE(pkg->name);
898 STRDUP(pkg->name, tmp, RET_ERR(PM_ERR_MEMORY, -1));
899 pkg->name_hash = _alpm_hash_sdbm(pkg->name);
901 free(tmp);
902 return(0);
906 * Hash the given string to an unsigned long value.
907 * This is the standard sdbm hashing algorithm.
908 * @param str string to hash
909 * @return the hash value of the given string
911 unsigned long _alpm_hash_sdbm(const char *str)
913 unsigned long hash = 0;
914 int c;
916 if(!str) {
917 return(hash);
919 while((c = *str++)) {
920 hash = c + (hash << 6) + (hash << 16) - hash;
923 return(hash);
926 /* vim: set ts=2 sw=2 noet: */