Read pkgcache into hash
[pacman-ng.git] / lib / libalpm / util.c
blob82387b2f88945ebca8e52036b3816c556dcbbd1a
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 <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>
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 "package.h"
57 #include "alpm.h"
58 #include "alpm_list.h"
59 #include "handle.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 pm_errno = PM_ERR_WRITE;
156 _alpm_log(PM_LOG_ERROR, _("error writing to file '%s': %s\n"),
157 dest, strerror(errno));
158 ret = -1;
159 goto cleanup;
163 /* chmod dest to permissions of src, as long as it is not a symlink */
164 struct stat statbuf;
165 if(!stat(src, &statbuf)) {
166 if(! S_ISLNK(statbuf.st_mode)) {
167 fchmod(fileno(out), statbuf.st_mode);
169 } else {
170 /* stat was unsuccessful */
171 ret = 1;
174 cleanup:
175 fclose(in);
176 fclose(out);
177 FREE(buf);
178 return(ret);
181 /* Trim whitespace and newlines from a string
183 char *_alpm_strtrim(char *str)
185 char *pch = str;
187 if(*str == '\0') {
188 /* string is empty, so we're done. */
189 return(str);
192 while(isspace((unsigned char)*pch)) {
193 pch++;
195 if(pch != str) {
196 memmove(str, pch, (strlen(pch) + 1));
199 /* check if there wasn't anything but whitespace in the string. */
200 if(*str == '\0') {
201 return(str);
204 pch = (str + (strlen(str) - 1));
205 while(isspace((unsigned char)*pch)) {
206 pch--;
208 *++pch = '\0';
210 return(str);
213 /* Create a lock file */
214 int _alpm_lckmk(void)
216 int fd;
217 char *dir, *ptr;
218 const char *file = alpm_option_get_lockfile();
220 /* create the dir of the lockfile first */
221 dir = strdup(file);
222 ptr = strrchr(dir, '/');
223 if(ptr) {
224 *ptr = '\0';
226 _alpm_makepath(dir);
227 FREE(dir);
229 do {
230 fd = open(file, O_WRONLY | O_CREAT | O_EXCL, 0000);
231 } while (fd == -1 && errno == EINTR);
232 if(fd > 0) {
233 FILE *f = fdopen(fd, "w");
234 fprintf(f, "%ld\n", (long)getpid());
235 fflush(f);
236 fsync(fd);
237 fclose(f);
238 return(fd);
240 return(-1);
243 /* Remove a lock file */
244 int _alpm_lckrm(void)
246 const char *file = alpm_option_get_lockfile();
247 if(unlink(file) == -1 && errno != ENOENT) {
248 return(-1);
250 return(0);
253 /* Compression functions */
256 * @brief Unpack a specific file in an archive.
258 * @param archive the archive to unpack
259 * @param prefix where to extract the files
260 * @param fn a file within the archive to unpack
261 * @return 0 on success, 1 on failure
263 int _alpm_unpack_single(const char *archive, const char *prefix, const char *fn)
265 alpm_list_t *list = NULL;
266 int ret = 0;
267 if(fn == NULL) {
268 return(1);
270 list = alpm_list_add(list, (void *)fn);
271 ret = _alpm_unpack(archive, prefix, list, 1);
272 alpm_list_free(list);
273 return(ret);
277 * @brief Unpack a list of files in an archive.
279 * @param archive the archive to unpack
280 * @param prefix where to extract the files
281 * @param list a list of files within the archive to unpack or
282 * NULL for all
283 * @param breakfirst break after the first entry found
285 * @return 0 on success, 1 on failure
287 int _alpm_unpack(const char *archive, const char *prefix, alpm_list_t *list, int breakfirst)
289 int ret = 0;
290 mode_t oldmask;
291 struct archive *_archive;
292 struct archive_entry *entry;
293 char cwd[PATH_MAX];
294 int restore_cwd = 0;
296 ALPM_LOG_FUNC;
298 if((_archive = archive_read_new()) == NULL)
299 RET_ERR(PM_ERR_LIBARCHIVE, 1);
301 archive_read_support_compression_all(_archive);
302 archive_read_support_format_all(_archive);
304 if(archive_read_open_filename(_archive, archive,
305 ARCHIVE_DEFAULT_BYTES_PER_BLOCK) != ARCHIVE_OK) {
306 _alpm_log(PM_LOG_ERROR, _("could not open %s: %s\n"), archive,
307 archive_error_string(_archive));
308 RET_ERR(PM_ERR_PKG_OPEN, 1);
311 oldmask = umask(0022);
313 /* save the cwd so we can restore it later */
314 if(getcwd(cwd, PATH_MAX) == NULL) {
315 _alpm_log(PM_LOG_ERROR, _("could not get current working directory\n"));
316 } else {
317 restore_cwd = 1;
320 /* just in case our cwd was removed in the upgrade operation */
321 if(chdir(prefix) != 0) {
322 _alpm_log(PM_LOG_ERROR, _("could not change directory to %s (%s)\n"), prefix, strerror(errno));
323 ret = 1;
324 goto cleanup;
327 while(archive_read_next_header(_archive, &entry) == ARCHIVE_OK) {
328 const struct stat *st;
329 const char *entryname; /* the name of the file in the archive */
331 st = archive_entry_stat(entry);
332 entryname = archive_entry_pathname(entry);
334 if(S_ISREG(st->st_mode)) {
335 archive_entry_set_perm(entry, 0644);
336 } else if(S_ISDIR(st->st_mode)) {
337 archive_entry_set_perm(entry, 0755);
340 /* If specific files were requested, skip entries that don't match. */
341 if(list) {
342 char *prefix = strdup(entryname);
343 char *p = strstr(prefix,"/");
344 if(p) {
345 *(p+1) = '\0';
347 char *found = alpm_list_find_str(list, prefix);
348 free(prefix);
349 if(!found) {
350 if (archive_read_data_skip(_archive) != ARCHIVE_OK) {
351 ret = 1;
352 goto cleanup;
354 continue;
355 } else {
356 _alpm_log(PM_LOG_DEBUG, "extracting: %s\n", entryname);
360 /* Extract the archive entry. */
361 int readret = archive_read_extract(_archive, entry, 0);
362 if(readret == ARCHIVE_WARN) {
363 /* operation succeeded but a non-critical error was encountered */
364 _alpm_log(PM_LOG_WARNING, _("warning given while extracting %s (%s)\n"),
365 entryname, archive_error_string(_archive));
366 } else if(readret != ARCHIVE_OK) {
367 _alpm_log(PM_LOG_ERROR, _("could not extract %s (%s)\n"),
368 entryname, archive_error_string(_archive));
369 ret = 1;
370 goto cleanup;
373 if(breakfirst) {
374 break;
378 cleanup:
379 umask(oldmask);
380 archive_read_finish(_archive);
381 if(restore_cwd && chdir(cwd) != 0) {
382 _alpm_log(PM_LOG_ERROR, _("could not change directory to %s (%s)\n"), cwd, strerror(errno));
384 return(ret);
387 /* does the same thing as 'rm -rf' */
388 int _alpm_rmrf(const char *path)
390 int errflag = 0;
391 struct dirent *dp;
392 DIR *dirp;
393 char name[PATH_MAX];
394 struct stat st;
396 if(_alpm_lstat(path, &st) == 0) {
397 if(!S_ISDIR(st.st_mode)) {
398 if(!unlink(path)) {
399 return(0);
400 } else {
401 if(errno == ENOENT) {
402 return(0);
403 } else {
404 return(1);
407 } else {
408 dirp = opendir(path);
409 if(!dirp) {
410 return(1);
412 for(dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
413 if(dp->d_ino) {
414 sprintf(name, "%s/%s", path, dp->d_name);
415 if(strcmp(dp->d_name, "..") != 0 && strcmp(dp->d_name, ".") != 0) {
416 errflag += _alpm_rmrf(name);
420 closedir(dirp);
421 if(rmdir(path)) {
422 errflag++;
425 return(errflag);
427 return(0);
430 int _alpm_logaction(int usesyslog, FILE *f, const char *fmt, va_list args)
432 int ret = 0;
434 if(usesyslog) {
435 /* we can't use a va_list more than once, so we need to copy it
436 * so we can use the original when calling vfprintf below. */
437 va_list args_syslog;
438 va_copy(args_syslog, args);
439 vsyslog(LOG_WARNING, fmt, args_syslog);
440 va_end(args_syslog);
443 if(f) {
444 time_t t;
445 struct tm *tm;
447 t = time(NULL);
448 tm = localtime(&t);
450 /* Use ISO-8601 date format */
451 fprintf(f, "[%04d-%02d-%02d %02d:%02d] ",
452 tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
453 tm->tm_hour, tm->tm_min);
454 ret = vfprintf(f, fmt, args);
455 fflush(f);
458 return(ret);
461 int _alpm_run_chroot(const char *root, const char *path, char *const argv[])
463 char cwd[PATH_MAX];
464 pid_t pid;
465 int pipefd[2];
466 int restore_cwd = 0;
467 int retval = 0;
469 ALPM_LOG_FUNC;
471 /* save the cwd so we can restore it later */
472 if(getcwd(cwd, PATH_MAX) == NULL) {
473 _alpm_log(PM_LOG_ERROR, _("could not get current working directory\n"));
474 } else {
475 restore_cwd = 1;
478 /* just in case our cwd was removed in the upgrade operation */
479 if(chdir(root) != 0) {
480 _alpm_log(PM_LOG_ERROR, _("could not change directory to %s (%s)\n"), root, strerror(errno));
481 goto cleanup;
484 _alpm_log(PM_LOG_DEBUG, "executing \"%s\" under chroot \"%s\"\n", path, root);
486 /* Flush open fds before fork() to avoid cloning buffers */
487 fflush(NULL);
489 if(pipe(pipefd) == -1) {
490 _alpm_log(PM_LOG_ERROR, _("could not create pipe (%s)\n"), strerror(errno));
491 retval = 1;
492 goto cleanup;
495 /* fork- parent and child each have seperate code blocks below */
496 pid = fork();
497 if(pid == -1) {
498 _alpm_log(PM_LOG_ERROR, _("could not fork a new process (%s)\n"), strerror(errno));
499 retval = 1;
500 goto cleanup;
503 if(pid == 0) {
504 /* this code runs for the child only (the actual chroot/exec) */
505 close(1);
506 close(2);
507 while(dup2(pipefd[1], 1) == -1 && errno == EINTR);
508 while(dup2(pipefd[1], 2) == -1 && errno == EINTR);
509 close(pipefd[0]);
510 close(pipefd[1]);
512 /* use fprintf instead of _alpm_log to send output through the parent */
513 if(chroot(root) != 0) {
514 fprintf(stderr, _("could not change the root directory (%s)\n"), strerror(errno));
515 exit(1);
517 if(chdir("/") != 0) {
518 fprintf(stderr, _("could not change directory to / (%s)\n"), strerror(errno));
519 exit(1);
521 umask(0022);
522 execv(path, argv);
523 fprintf(stderr, _("call to execv failed (%s)\n"), strerror(errno));
524 exit(1);
525 } else {
526 /* this code runs for the parent only (wait on the child) */
527 int status;
528 FILE *pipe;
530 close(pipefd[1]);
531 pipe = fdopen(pipefd[0], "r");
532 if(pipe == NULL) {
533 close(pipefd[0]);
534 retval = 1;
535 } else {
536 while(!feof(pipe)) {
537 char line[PATH_MAX];
538 if(fgets(line, PATH_MAX, pipe) == NULL)
539 break;
540 alpm_logaction("%s", line);
541 EVENT(handle->trans, PM_TRANS_EVT_SCRIPTLET_INFO, line, NULL);
543 fclose(pipe);
546 while(waitpid(pid, &status, 0) == -1) {
547 if(errno != EINTR) {
548 _alpm_log(PM_LOG_ERROR, _("call to waitpid failed (%s)\n"), strerror(errno));
549 retval = 1;
550 goto cleanup;
554 /* report error from above after the child has exited */
555 if(retval != 0) {
556 _alpm_log(PM_LOG_ERROR, _("could not open pipe (%s)\n"), strerror(errno));
557 goto cleanup;
559 /* check the return status, make sure it is 0 (success) */
560 if(WIFEXITED(status)) {
561 _alpm_log(PM_LOG_DEBUG, "call to waitpid succeeded\n");
562 if(WEXITSTATUS(status) != 0) {
563 _alpm_log(PM_LOG_ERROR, _("command failed to execute correctly\n"));
564 retval = 1;
569 cleanup:
570 if(restore_cwd && chdir(cwd) != 0) {
571 _alpm_log(PM_LOG_ERROR, _("could not change directory to %s (%s)\n"), cwd, strerror(errno));
574 return(retval);
577 int _alpm_ldconfig(const char *root)
579 char line[PATH_MAX];
581 _alpm_log(PM_LOG_DEBUG, "running ldconfig\n");
583 snprintf(line, PATH_MAX, "%setc/ld.so.conf", root);
584 if(access(line, F_OK) == 0) {
585 snprintf(line, PATH_MAX, "%ssbin/ldconfig", root);
586 if(access(line, X_OK) == 0) {
587 char *argv[] = { "ldconfig", NULL };
588 _alpm_run_chroot(root, "/sbin/ldconfig", argv);
592 return(0);
595 /* Helper function for comparing strings using the
596 * alpm "compare func" signature */
597 int _alpm_str_cmp(const void *s1, const void *s2)
599 return(strcmp(s1, s2));
602 /** Find a filename in a registered alpm cachedir.
603 * @param filename name of file to find
604 * @return malloced path of file, NULL if not found
606 char *_alpm_filecache_find(const char* filename)
608 char path[PATH_MAX];
609 char *retpath;
610 alpm_list_t *i;
612 /* Loop through the cache dirs until we find a matching file */
613 for(i = alpm_option_get_cachedirs(); i; i = alpm_list_next(i)) {
614 snprintf(path, PATH_MAX, "%s%s", (char*)alpm_list_getdata(i),
615 filename);
616 if(access(path, R_OK) == 0) {
617 retpath = strdup(path);
618 _alpm_log(PM_LOG_DEBUG, "found cached pkg: %s\n", retpath);
619 return(retpath);
622 /* package wasn't found in any cachedir */
623 return(NULL);
626 /** Check the alpm cachedirs for existance and find a writable one.
627 * If no valid cache directory can be found, use /tmp.
628 * @return pointer to a writable cache directory.
630 const char *_alpm_filecache_setup(void)
632 struct stat buf;
633 alpm_list_t *i, *tmp;
634 char *cachedir;
636 /* Loop through the cache dirs until we find a writeable dir */
637 for(i = alpm_option_get_cachedirs(); i; i = alpm_list_next(i)) {
638 cachedir = alpm_list_getdata(i);
639 if(stat(cachedir, &buf) != 0) {
640 /* cache directory does not exist.... try creating it */
641 _alpm_log(PM_LOG_WARNING, _("no %s cache exists, creating...\n"),
642 cachedir);
643 if(_alpm_makepath(cachedir) == 0) {
644 _alpm_log(PM_LOG_DEBUG, "using cachedir: %s\n", cachedir);
645 return(cachedir);
647 } else if(S_ISDIR(buf.st_mode) && (buf.st_mode & S_IWUSR)) {
648 _alpm_log(PM_LOG_DEBUG, "using cachedir: %s\n", cachedir);
649 return(cachedir);
653 /* we didn't find a valid cache directory. use /tmp. */
654 tmp = alpm_list_add(NULL, strdup("/tmp/"));
655 alpm_option_set_cachedirs(tmp);
656 _alpm_log(PM_LOG_DEBUG, "using cachedir: %s", "/tmp/\n");
657 _alpm_log(PM_LOG_WARNING, _("couldn't create package cache, using /tmp instead\n"));
658 return(alpm_list_getdata(tmp));
661 /** lstat wrapper that treats /path/dirsymlink/ the same as /path/dirsymlink.
662 * Linux lstat follows POSIX semantics and still performs a dereference on
663 * the first, and for uses of lstat in libalpm this is not what we want.
664 * @param path path to file to lstat
665 * @param buf structure to fill with stat information
666 * @return the return code from lstat
668 int _alpm_lstat(const char *path, struct stat *buf)
670 int ret;
671 char *newpath = strdup(path);
672 size_t len = strlen(newpath);
674 /* strip the trailing slash if one exists */
675 if(len != 0 && newpath[len - 1] == '/') {
676 newpath[len - 1] = '\0';
679 ret = lstat(newpath, buf);
681 FREE(newpath);
682 return(ret);
685 #ifdef HAVE_LIBSSL
686 static int md5_file(const char *path, unsigned char output[16])
688 FILE *f;
689 size_t n;
690 MD5_CTX ctx;
691 unsigned char *buf;
693 CALLOC(buf, 8192, sizeof(unsigned char), return(1));
695 if((f = fopen(path, "rb")) == NULL) {
696 free(buf);
697 return(1);
700 MD5_Init(&ctx);
702 while((n = fread(buf, 1, sizeof(buf), f)) > 0) {
703 MD5_Update(&ctx, buf, n);
706 MD5_Final(output, &ctx);
708 memset(&ctx, 0, sizeof(MD5_CTX));
709 free(buf);
711 if(ferror(f) != 0) {
712 fclose(f);
713 return(2);
716 fclose(f);
717 return(0);
719 #endif
721 /** Get the md5 sum of file.
722 * @param filename name of the file
723 * @return the checksum on success, NULL on error
724 * @addtogroup alpm_misc
726 char SYMEXPORT *alpm_compute_md5sum(const char *filename)
728 unsigned char output[16];
729 char *md5sum;
730 int ret, i;
732 ALPM_LOG_FUNC;
734 ASSERT(filename != NULL, return(NULL));
736 /* allocate 32 chars plus 1 for null */
737 md5sum = calloc(33, sizeof(char));
738 /* defined above for OpenSSL, otherwise defined in md5.h */
739 ret = md5_file(filename, output);
741 if (ret > 0) {
742 RET_ERR(PM_ERR_NOT_A_FILE, NULL);
745 /* Convert the result to something readable */
746 for (i = 0; i < 16; i++) {
747 /* sprintf is acceptable here because we know our output */
748 sprintf(md5sum +(i * 2), "%02x", output[i]);
750 md5sum[32] = '\0';
752 _alpm_log(PM_LOG_DEBUG, "md5(%s) = %s\n", filename, md5sum);
753 return(md5sum);
756 int _alpm_test_md5sum(const char *filepath, const char *md5sum)
758 char *md5sum2;
759 int ret;
761 md5sum2 = alpm_compute_md5sum(filepath);
763 if(md5sum == NULL || md5sum2 == NULL) {
764 ret = -1;
765 } else if(strcmp(md5sum, md5sum2) != 0) {
766 ret = 1;
767 } else {
768 ret = 0;
771 FREE(md5sum2);
772 return(ret);
775 /* Note: does NOT handle sparse files on purpose for speed. */
776 int _alpm_archive_fgets(struct archive *a, struct archive_read_buffer *b)
778 char *i = NULL;
779 int64_t offset;
780 int done = 0;
782 while(1) {
783 /* have we processed this entire block? */
784 if(b->block + b->block_size == b->block_offset) {
785 if(b->ret == ARCHIVE_EOF) {
786 /* reached end of archive on the last read, now we are out of data */
787 goto cleanup;
790 /* zero-copy - this is the entire next block of data. */
791 b->ret = archive_read_data_block(a, (void*)&b->block,
792 &b->block_size, &offset);
793 b->block_offset = b->block;
795 /* error or end of archive with no data read, cleanup */
796 if(b->ret < ARCHIVE_OK ||
797 (b->block_size == 0 && b->ret == ARCHIVE_EOF)) {
798 goto cleanup;
802 /* loop through the block looking for EOL characters */
803 for(i = b->block_offset; i < (b->block + b->block_size); i++) {
804 /* check if read value was null or newline */
805 if(*i == '\0' || *i == '\n') {
806 done = 1;
807 break;
811 /* allocate our buffer, or ensure our existing one is big enough */
812 if(!b->line) {
813 /* set the initial buffer to the read block_size */
814 CALLOC(b->line, b->block_size + 1, sizeof(char),
815 RET_ERR(PM_ERR_MEMORY, -1));
816 b->line_size = b->block_size + 1;
817 b->line_offset = b->line;
818 } else {
819 size_t needed = (size_t)((b->line_offset - b->line)
820 + (i - b->block_offset) + 1);
821 if(needed > b->max_line_size) {
822 RET_ERR(PM_ERR_MEMORY, -1);
824 if(needed > b->line_size) {
825 /* need to realloc + copy data to fit total length */
826 char *new;
827 CALLOC(new, needed, sizeof(char), RET_ERR(PM_ERR_MEMORY, -1));
828 memcpy(new, b->line, b->line_size);
829 b->line_size = needed;
830 b->line_offset = new + (b->line_offset - b->line);
831 free(b->line);
832 b->line = new;
836 if(done) {
837 size_t len = (size_t)(i - b->block_offset);
838 memcpy(b->line_offset, b->block_offset, len);
839 b->line_offset[len] = '\0';
840 b->block_offset = ++i;
841 /* this is the main return point; from here you can read b->line */
842 return(ARCHIVE_OK);
843 } else {
844 /* we've looked through the whole block but no newline, copy it */
845 size_t len = (size_t)(b->block + b->block_size - b->block_offset);
846 memcpy(b->line_offset, b->block_offset, len);
847 b->line_offset += len;
848 b->block_offset = i;
852 cleanup:
854 int ret = b->ret;
855 FREE(b->line);
856 memset(b, 0, sizeof(b));
857 return(ret);
861 int _alpm_splitname(const char *target, pmpkg_t *pkg)
863 /* the format of a db entry is as follows:
864 * package-version-rel/
865 * package name can contain hyphens, so parse from the back- go back
866 * two hyphens and we have split the version from the name.
868 const char *version, *end;
870 if(target == NULL || pkg == NULL) {
871 return(-1);
873 end = target + strlen(target);
875 /* remove any trailing '/' */
876 while (*(end - 1) == '/') {
877 --end;
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(version = end - 1; *version && *version != '-'; version--);
883 for(version = version - 1; *version && *version != '-'; version--);
884 if(*version != '-' || version == target) {
885 return(-1);
888 /* copy into fields and return */
889 if(pkg->version) {
890 FREE(pkg->version);
892 /* version actually points to the dash, so need to increment 1 and account
893 * for potential end character */
894 STRNDUP(pkg->version, version + 1, end - version - 1,
895 RET_ERR(PM_ERR_MEMORY, -1));
897 if(pkg->name) {
898 FREE(pkg->name);
900 STRNDUP(pkg->name, target, version - target, RET_ERR(PM_ERR_MEMORY, -1));
901 pkg->name_hash = _alpm_hash_sdbm(pkg->name);
903 return(0);
907 * Hash the given string to an unsigned long value.
908 * This is the standard sdbm hashing algorithm.
909 * @param str string to hash
910 * @return the hash value of the given string
912 unsigned long _alpm_hash_sdbm(const char *str)
914 unsigned long hash = 0;
915 int c;
917 if(!str) {
918 return(hash);
920 while((c = *str++)) {
921 hash = c + (hash << 6) + (hash << 16) - hash;
924 return(hash);
927 long _alpm_parsedate(const char *line)
929 if(isalpha((unsigned char)line[0])) {
930 /* initialize to null in case of failure */
931 struct tm tmp_tm = { 0 };
932 setlocale(LC_TIME, "C");
933 strptime(line, "%a %b %e %H:%M:%S %Y", &tmp_tm);
934 setlocale(LC_TIME, "");
935 return(mktime(&tmp_tm));
937 return(atol(line));
940 #ifndef HAVE_STRNDUP
941 /* A quick and dirty implementation derived from glibc */
942 static size_t strnlen(const char *s, size_t max)
944 register const char *p;
945 for(p = s; *p && max--; ++p);
946 return(p - s);
949 char *strndup(const char *s, size_t n)
951 size_t len = strnlen(s, n);
952 char *new = (char *) malloc(len + 1);
954 if (new == NULL)
955 return NULL;
957 new[len] = '\0';
958 return (char *) memcpy(new, s, len);
960 #endif
962 /* vim: set ts=2 sw=2 noet: */