Move changelog functions to callback struct
[pacman-ng.git] / lib / libalpm / be_files.c
blob737f22c8ce73089c5fd76758c5c3bceb512877c6
1 /*
2 * be_files.c
4 * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org>
5 * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "config.h"
23 #include <unistd.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <errno.h>
27 #include <string.h>
28 #include <stdint.h> /* intmax_t */
29 #include <sys/stat.h>
30 #include <dirent.h>
31 #include <ctype.h>
32 #include <time.h>
33 #include <limits.h> /* PATH_MAX */
34 #include <locale.h> /* setlocale */
36 /* libarchive */
37 #include <archive.h>
38 #include <archive_entry.h>
40 /* libalpm */
41 #include "db.h"
42 #include "alpm_list.h"
43 #include "cache.h"
44 #include "log.h"
45 #include "util.h"
46 #include "alpm.h"
47 #include "handle.h"
48 #include "package.h"
49 #include "delta.h"
50 #include "deps.h"
51 #include "dload.h"
54 #define LAZY_LOAD(info, errret) \
55 do { \
56 ALPM_LOG_FUNC; \
57 ASSERT(handle != NULL, return(errret)); \
58 ASSERT(pkg != NULL, return(errret)); \
59 if(pkg->origin != PKG_FROM_FILE && !(pkg->infolevel & info)) { \
60 _alpm_db_read(pkg->origin_data.db, pkg, info); \
61 } \
62 } while(0)
65 /* Cache-specific accessor functions. These implementations allow for lazy
66 * loading by the files backend when a data member is actually needed
67 * rather than loading all pieces of information when the package is first
68 * initialized.
71 const char *_cache_get_filename(pmpkg_t *pkg)
73 LAZY_LOAD(INFRQ_DESC, NULL);
74 return pkg->filename;
77 const char *_cache_get_name(pmpkg_t *pkg)
79 ASSERT(pkg != NULL, return(NULL));
80 return pkg->name;
83 static const char *_cache_get_version(pmpkg_t *pkg)
85 ASSERT(pkg != NULL, return(NULL));
86 return pkg->version;
89 static const char *_cache_get_desc(pmpkg_t *pkg)
91 LAZY_LOAD(INFRQ_DESC, NULL);
92 return pkg->desc;
95 const char *_cache_get_url(pmpkg_t *pkg)
97 LAZY_LOAD(INFRQ_DESC, NULL);
98 return pkg->url;
101 time_t _cache_get_builddate(pmpkg_t *pkg)
103 LAZY_LOAD(INFRQ_DESC, 0);
104 return pkg->builddate;
107 time_t _cache_get_installdate(pmpkg_t *pkg)
109 LAZY_LOAD(INFRQ_DESC, 0);
110 return pkg->installdate;
113 const char *_cache_get_packager(pmpkg_t *pkg)
115 LAZY_LOAD(INFRQ_DESC, NULL);
116 return pkg->packager;
119 const char *_cache_get_md5sum(pmpkg_t *pkg)
121 LAZY_LOAD(INFRQ_DESC, NULL);
122 return pkg->md5sum;
125 const char *_cache_get_arch(pmpkg_t *pkg)
127 LAZY_LOAD(INFRQ_DESC, NULL);
128 return pkg->arch;
131 off_t _cache_get_size(pmpkg_t *pkg)
133 LAZY_LOAD(INFRQ_DESC, -1);
134 return pkg->size;
137 off_t _cache_get_isize(pmpkg_t *pkg)
139 LAZY_LOAD(INFRQ_DESC, -1);
140 return pkg->isize;
143 pmpkgreason_t _cache_get_reason(pmpkg_t *pkg)
145 LAZY_LOAD(INFRQ_DESC, -1);
146 return pkg->reason;
149 alpm_list_t *_cache_get_licenses(pmpkg_t *pkg)
151 LAZY_LOAD(INFRQ_DESC, NULL);
152 return pkg->licenses;
155 alpm_list_t *_cache_get_groups(pmpkg_t *pkg)
157 LAZY_LOAD(INFRQ_DESC, NULL);
158 return pkg->groups;
161 int _cache_has_force(pmpkg_t *pkg)
163 LAZY_LOAD(INFRQ_DESC, -1);
164 return pkg->force;
167 alpm_list_t *_cache_get_depends(pmpkg_t *pkg)
169 LAZY_LOAD(INFRQ_DEPENDS, NULL);
170 return pkg->depends;
173 alpm_list_t *_cache_get_optdepends(pmpkg_t *pkg)
175 LAZY_LOAD(INFRQ_DEPENDS, NULL);
176 return pkg->optdepends;
179 alpm_list_t *_cache_get_conflicts(pmpkg_t *pkg)
181 LAZY_LOAD(INFRQ_DEPENDS, NULL);
182 return pkg->conflicts;
185 alpm_list_t *_cache_get_provides(pmpkg_t *pkg)
187 LAZY_LOAD(INFRQ_DEPENDS, NULL);
188 return pkg->provides;
191 alpm_list_t *_cache_get_replaces(pmpkg_t *pkg)
193 LAZY_LOAD(INFRQ_DESC, NULL);
194 return pkg->replaces;
197 alpm_list_t *_cache_get_deltas(pmpkg_t *pkg)
199 LAZY_LOAD(INFRQ_DELTAS, NULL);
200 return pkg->deltas;
203 alpm_list_t *_cache_get_files(pmpkg_t *pkg)
205 ALPM_LOG_FUNC;
207 /* Sanity checks */
208 ASSERT(handle != NULL, return(NULL));
209 ASSERT(pkg != NULL, return(NULL));
211 if(pkg->origin == PKG_FROM_LOCALDB
212 && !(pkg->infolevel & INFRQ_FILES)) {
213 _alpm_db_read(pkg->origin_data.db, pkg, INFRQ_FILES);
215 return pkg->files;
218 alpm_list_t *_cache_get_backup(pmpkg_t *pkg)
220 ALPM_LOG_FUNC;
222 /* Sanity checks */
223 ASSERT(handle != NULL, return(NULL));
224 ASSERT(pkg != NULL, return(NULL));
226 if(pkg->origin == PKG_FROM_LOCALDB
227 && !(pkg->infolevel & INFRQ_FILES)) {
228 _alpm_db_read(pkg->origin_data.db, pkg, INFRQ_FILES);
230 return pkg->backup;
234 * Open a package changelog for reading. Similar to fopen in functionality,
235 * except that the returned 'file stream' is from the database.
236 * @param pkg the package (from db) to read the changelog
237 * @return a 'file stream' to the package changelog
239 void *_cache_changelog_open(pmpkg_t *pkg)
241 ALPM_LOG_FUNC;
243 /* Sanity checks */
244 ASSERT(handle != NULL, return(NULL));
245 ASSERT(pkg != NULL, return(NULL));
247 char clfile[PATH_MAX];
248 snprintf(clfile, PATH_MAX, "%s/%s/%s-%s/changelog",
249 alpm_option_get_dbpath(),
250 alpm_db_get_name(alpm_pkg_get_db(pkg)),
251 alpm_pkg_get_name(pkg),
252 alpm_pkg_get_version(pkg));
253 return fopen(clfile, "r");
257 * Read data from an open changelog 'file stream'. Similar to fread in
258 * functionality, this function takes a buffer and amount of data to read.
259 * @param ptr a buffer to fill with raw changelog data
260 * @param size the size of the buffer
261 * @param pkg the package that the changelog is being read from
262 * @param fp a 'file stream' to the package changelog
263 * @return the number of characters read, or 0 if there is no more data
265 size_t _cache_changelog_read(void *ptr, size_t size,
266 const pmpkg_t *pkg, const void *fp)
268 return ( fread(ptr, 1, size, (FILE*)fp) );
272 int _cache_changelog_feof(const pmpkg_t *pkg, void *fp)
274 return( feof((FILE*)fp) );
279 * Close a package changelog for reading. Similar to fclose in functionality,
280 * except that the 'file stream' is from the database.
281 * @param pkg the package that the changelog was read from
282 * @param fp a 'file stream' to the package changelog
283 * @return whether closing the package changelog stream was successful
285 int _cache_changelog_close(const pmpkg_t *pkg, void *fp)
287 return( fclose((FILE*)fp) );
290 /** The sync database operations struct. Get package fields through
291 * lazy accessor methods that handle any backend loading and caching
292 * logic.
294 static struct pkg_operations sync_pkg_ops = {
295 .get_filename = _cache_get_filename,
296 .get_name = _cache_get_name,
297 .get_version = _cache_get_version,
298 .get_desc = _cache_get_desc,
299 .get_url = _cache_get_url,
300 .get_builddate = _cache_get_builddate,
301 .get_installdate = _cache_get_installdate,
302 .get_packager = _cache_get_packager,
303 .get_md5sum = _cache_get_md5sum,
304 .get_arch = _cache_get_arch,
305 .get_size = _cache_get_size,
306 .get_isize = _cache_get_isize,
307 .get_reason = _cache_get_reason,
308 .has_force = _cache_has_force,
309 .get_licenses = _cache_get_licenses,
310 .get_groups = _cache_get_groups,
311 .get_depends = _cache_get_depends,
312 .get_optdepends = _cache_get_optdepends,
313 .get_conflicts = _cache_get_conflicts,
314 .get_provides = _cache_get_provides,
315 .get_replaces = _cache_get_replaces,
316 .get_deltas = _cache_get_deltas,
317 .get_files = _cache_get_files,
318 .get_backup = _cache_get_backup,
321 /** The local database operations struct. Get package fields through
322 * lazy accessor methods that handle any backend loading and caching
323 * logic.
325 static struct pkg_operations local_pkg_ops = {
326 .get_filename = _cache_get_filename,
327 .get_name = _cache_get_name,
328 .get_version = _cache_get_version,
329 .get_desc = _cache_get_desc,
330 .get_url = _cache_get_url,
331 .get_builddate = _cache_get_builddate,
332 .get_installdate = _cache_get_installdate,
333 .get_packager = _cache_get_packager,
334 .get_md5sum = _cache_get_md5sum,
335 .get_arch = _cache_get_arch,
336 .get_size = _cache_get_size,
337 .get_isize = _cache_get_isize,
338 .get_reason = _cache_get_reason,
339 .has_force = _cache_has_force,
340 .get_licenses = _cache_get_licenses,
341 .get_groups = _cache_get_groups,
342 .get_depends = _cache_get_depends,
343 .get_optdepends = _cache_get_optdepends,
344 .get_conflicts = _cache_get_conflicts,
345 .get_provides = _cache_get_provides,
346 .get_replaces = _cache_get_replaces,
347 .get_deltas = _cache_get_deltas,
348 .get_files = _cache_get_files,
349 .get_backup = _cache_get_backup,
351 .changelog_open = _cache_changelog_open,
352 .changelog_read = _cache_changelog_read,
353 .changelog_close = _cache_changelog_close,
356 static int checkdbdir(pmdb_t *db)
358 struct stat buf;
359 const char *path = _alpm_db_path(db);
361 if(stat(path, &buf) != 0) {
362 _alpm_log(PM_LOG_DEBUG, "database dir '%s' does not exist, creating it\n",
363 path);
364 if(_alpm_makepath(path) != 0) {
365 RET_ERR(PM_ERR_SYSTEM, -1);
367 } else if(!S_ISDIR(buf.st_mode)) {
368 _alpm_log(PM_LOG_WARNING, _("removing invalid database: %s\n"), path);
369 if(unlink(path) != 0 || _alpm_makepath(path) != 0) {
370 RET_ERR(PM_ERR_SYSTEM, -1);
373 return(0);
376 /* create list of directories in db */
377 static int dirlist_from_tar(const char *archive, alpm_list_t **dirlist)
379 struct archive *_archive;
380 struct archive_entry *entry;
382 if((_archive = archive_read_new()) == NULL)
383 RET_ERR(PM_ERR_LIBARCHIVE, -1);
385 archive_read_support_compression_all(_archive);
386 archive_read_support_format_all(_archive);
388 if(archive_read_open_filename(_archive, archive,
389 ARCHIVE_DEFAULT_BYTES_PER_BLOCK) != ARCHIVE_OK) {
390 _alpm_log(PM_LOG_ERROR, _("could not open %s: %s\n"), archive,
391 archive_error_string(_archive));
392 RET_ERR(PM_ERR_PKG_OPEN, -1);
395 while(archive_read_next_header(_archive, &entry) == ARCHIVE_OK) {
396 const struct stat *st;
397 const char *entryname; /* the name of the file in the archive */
399 st = archive_entry_stat(entry);
400 entryname = archive_entry_pathname(entry);
402 if(S_ISDIR(st->st_mode)) {
403 char *name = strdup(entryname);
404 *dirlist = alpm_list_add(*dirlist, name);
407 archive_read_finish(_archive);
409 *dirlist = alpm_list_msort(*dirlist, alpm_list_count(*dirlist), _alpm_str_cmp);
410 return(0);
413 static int is_dir(const char *path, struct dirent *entry)
415 #ifdef DT_DIR
416 return(entry->d_type == DT_DIR);
417 #else
418 char buffer[PATH_MAX];
419 snprintf(buffer, PATH_MAX, "%s/%s", path, entry->d_name);
421 struct stat sbuf;
422 if (!stat(buffer, &sbuf)) {
423 return(S_ISDIR(sbuf.st_mode));
426 return(0);
427 #endif
430 /* create list of directories in db */
431 static int dirlist_from_fs(const char *syncdbpath, alpm_list_t **dirlist)
433 DIR *dbdir;
434 struct dirent *ent = NULL;
436 dbdir = opendir(syncdbpath);
437 if (dbdir != NULL) {
438 while((ent = readdir(dbdir)) != NULL) {
439 char *name = ent->d_name;
440 size_t len;
441 char *entry;
443 if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
444 continue;
447 if(!is_dir(syncdbpath, ent)) {
448 continue;
451 len = strlen(name);
452 MALLOC(entry, len + 2, RET_ERR(PM_ERR_MEMORY, -1));
453 strcpy(entry, name);
454 entry[len] = '/';
455 entry[len+1] = '\0';
456 *dirlist = alpm_list_add(*dirlist, entry);
458 closedir(dbdir);
461 *dirlist = alpm_list_msort(*dirlist, alpm_list_count(*dirlist), _alpm_str_cmp);
462 return(0);
465 /* remove old directories from dbdir */
466 static int remove_olddir(const char *syncdbpath, alpm_list_t *dirlist)
468 alpm_list_t *i;
469 for (i = dirlist; i; i = i->next) {
470 const char *name = i->data;
471 char *dbdir;
472 size_t len = strlen(syncdbpath) + strlen(name) + 2;
473 MALLOC(dbdir, len, RET_ERR(PM_ERR_MEMORY, -1));
474 snprintf(dbdir, len, "%s%s", syncdbpath, name);
475 _alpm_log(PM_LOG_DEBUG, "removing: %s\n", dbdir);
476 if(_alpm_rmrf(dbdir) != 0) {
477 _alpm_log(PM_LOG_ERROR, _("could not remove database directory %s\n"), dbdir);
478 free(dbdir);
479 RET_ERR(PM_ERR_DB_REMOVE, -1);
481 free(dbdir);
483 return(0);
486 /** Update a package database
488 * An update of the package database \a db will be attempted. Unless
489 * \a force is true, the update will only be performed if the remote
490 * database was modified since the last update.
492 * A transaction is necessary for this operation, in order to obtain a
493 * database lock. During this transaction the front-end will be informed
494 * of the download progress of the database via the download callback.
496 * Example:
497 * @code
498 * pmdb_t *db;
499 * int result;
500 * db = alpm_list_getdata(alpm_option_get_syncdbs());
501 * if(alpm_trans_init(0, NULL, NULL, NULL) == 0) {
502 * result = alpm_db_update(0, db);
503 * alpm_trans_release();
505 * if(result > 0) {
506 * printf("Unable to update database: %s\n", alpm_strerrorlast());
507 * } else if(result < 0) {
508 * printf("Database already up to date\n");
509 * } else {
510 * printf("Database updated\n");
513 * @endcode
515 * @ingroup alpm_databases
516 * @note After a successful update, the \link alpm_db_get_pkgcache()
517 * package cache \endlink will be invalidated
518 * @param force if true, then forces the update, otherwise update only in case
519 * the database isn't up to date
520 * @param db pointer to the package database to update
521 * @return 0 on success, > 0 on error (pm_errno is set accordingly), < 0 if up
522 * to date
524 int SYMEXPORT alpm_db_update(int force, pmdb_t *db)
526 char *dbfile, *dbfilepath, *syncpath;
527 const char *dbpath, *syncdbpath;
528 alpm_list_t *newdirlist = NULL, *olddirlist = NULL;
529 alpm_list_t *onlynew = NULL, *onlyold = NULL;
530 size_t len;
531 int ret;
533 ALPM_LOG_FUNC;
535 /* Sanity checks */
536 ASSERT(handle != NULL, RET_ERR(PM_ERR_HANDLE_NULL, -1));
537 ASSERT(db != NULL && db != handle->db_local, RET_ERR(PM_ERR_WRONG_ARGS, -1));
538 /* Verify we are in a transaction. This is done _mainly_ because we need a DB
539 * lock - if we update without a db lock, we may kludge some other pacman
540 * process that _has_ a lock.
542 ASSERT(handle->trans != NULL, RET_ERR(PM_ERR_TRANS_NULL, -1));
543 ASSERT(handle->trans->state == STATE_INITIALIZED, RET_ERR(PM_ERR_TRANS_NOT_INITIALIZED, -1));
545 if(!alpm_list_find_ptr(handle->dbs_sync, db)) {
546 RET_ERR(PM_ERR_DB_NOT_FOUND, -1);
549 len = strlen(db->treename) + 4;
550 MALLOC(dbfile, len, RET_ERR(PM_ERR_MEMORY, -1));
551 sprintf(dbfile, "%s.db", db->treename);
553 dbpath = alpm_option_get_dbpath();
554 len = strlen(dbpath) + 6;
555 MALLOC(syncpath, len, RET_ERR(PM_ERR_MEMORY, -1));
556 sprintf(syncpath, "%s%s", dbpath, "sync/");
558 ret = _alpm_download_single_file(dbfile, db->servers, syncpath, force);
559 free(dbfile);
560 free(syncpath);
562 if(ret == 1) {
563 /* files match, do nothing */
564 pm_errno = 0;
565 return(1);
566 } else if(ret == -1) {
567 /* pm_errno was set by the download code */
568 _alpm_log(PM_LOG_DEBUG, "failed to sync db: %s\n", alpm_strerrorlast());
569 return(-1);
572 syncdbpath = _alpm_db_path(db);
574 /* form the path to the db location */
575 len = strlen(dbpath) + strlen(db->treename) + 9;
576 MALLOC(dbfilepath, len, RET_ERR(PM_ERR_MEMORY, -1));
577 sprintf(dbfilepath, "%ssync/%s.db", dbpath, db->treename);
579 if(force) {
580 /* if forcing update, remove the old dir and extract the db */
581 if(_alpm_rmrf(syncdbpath) != 0) {
582 _alpm_log(PM_LOG_ERROR, _("could not remove database %s\n"), db->treename);
583 RET_ERR(PM_ERR_DB_REMOVE, -1);
584 } else {
585 _alpm_log(PM_LOG_DEBUG, "database dir %s removed\n", _alpm_db_path(db));
587 } else {
588 /* if not forcing, only remove and extract what is necessary */
589 ret = dirlist_from_tar(dbfilepath, &newdirlist);
590 if(ret) {
591 goto cleanup;
593 ret = dirlist_from_fs(syncdbpath, &olddirlist);
594 if(ret) {
595 goto cleanup;
598 alpm_list_diff_sorted(olddirlist, newdirlist, _alpm_str_cmp, &onlyold, &onlynew);
600 ret = remove_olddir(syncdbpath, onlyold);
601 if(ret) {
602 goto cleanup;
606 /* Cache needs to be rebuilt */
607 _alpm_db_free_pkgcache(db);
609 checkdbdir(db);
610 ret = _alpm_unpack(dbfilepath, syncdbpath, onlynew, 0);
612 cleanup:
613 FREELIST(newdirlist);
614 FREELIST(olddirlist);
615 alpm_list_free(onlynew);
616 alpm_list_free(onlyold);
618 free(dbfilepath);
620 if(ret) {
621 RET_ERR(PM_ERR_SYSTEM, -1);
624 return(0);
628 static int splitname(const char *target, pmpkg_t *pkg)
630 /* the format of a db entry is as follows:
631 * package-version-rel/
632 * package name can contain hyphens, so parse from the back- go back
633 * two hyphens and we have split the version from the name.
635 char *tmp, *p, *q;
637 if(target == NULL || pkg == NULL) {
638 return(-1);
640 STRDUP(tmp, target, RET_ERR(PM_ERR_MEMORY, -1));
641 p = tmp + strlen(tmp);
643 /* do the magic parsing- find the beginning of the version string
644 * by doing two iterations of same loop to lop off two hyphens */
645 for(q = --p; *q && *q != '-'; q--);
646 for(p = --q; *p && *p != '-'; p--);
647 if(*p != '-' || p == tmp) {
648 return(-1);
651 /* copy into fields and return */
652 if(pkg->version) {
653 FREE(pkg->version);
655 STRDUP(pkg->version, p+1, RET_ERR(PM_ERR_MEMORY, -1));
656 /* insert a terminator at the end of the name (on hyphen)- then copy it */
657 *p = '\0';
658 if(pkg->name) {
659 FREE(pkg->name);
661 STRDUP(pkg->name, tmp, RET_ERR(PM_ERR_MEMORY, -1));
663 free(tmp);
664 return(0);
667 int _alpm_db_populate(pmdb_t *db)
669 int count = 0;
670 struct dirent *ent = NULL;
671 const char *dbpath;
672 DIR *dbdir;
674 ALPM_LOG_FUNC;
676 ASSERT(db != NULL, RET_ERR(PM_ERR_DB_NULL, -1));
678 dbpath = _alpm_db_path(db);
679 dbdir = opendir(dbpath);
680 if(dbdir == NULL) {
681 return(0);
683 while((ent = readdir(dbdir)) != NULL) {
684 const char *name = ent->d_name;
685 pmpkg_t *pkg;
687 if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
688 continue;
690 if(!is_dir(dbpath, ent)) {
691 continue;
694 pkg = _alpm_pkg_new();
695 if(pkg == NULL) {
696 closedir(dbdir);
697 return(-1);
699 /* split the db entry name */
700 if(splitname(name, pkg) != 0) {
701 _alpm_log(PM_LOG_ERROR, _("invalid name for database entry '%s'\n"),
702 name);
703 _alpm_pkg_free(pkg);
704 continue;
707 /* duplicated database entries are not allowed */
708 if(_alpm_pkg_find(db->pkgcache, pkg->name)) {
709 _alpm_log(PM_LOG_ERROR, _("duplicated database entry '%s'\n"), pkg->name);
710 _alpm_pkg_free(pkg);
711 continue;
714 /* explicitly read with only 'BASE' data, accessors will handle the rest */
715 if(_alpm_db_read(db, pkg, INFRQ_BASE) == -1) {
716 _alpm_log(PM_LOG_ERROR, _("corrupted database entry '%s'\n"), name);
717 _alpm_pkg_free(pkg);
718 continue;
720 if(db == handle->db_local) {
721 pkg->origin = PKG_FROM_LOCALDB;
722 pkg->ops = &local_pkg_ops;
723 } else {
724 pkg->origin = PKG_FROM_SYNCDB;
725 pkg->ops = &sync_pkg_ops;
727 pkg->origin_data.db = db;
728 /* add to the collection */
729 _alpm_log(PM_LOG_FUNCTION, "adding '%s' to package cache for db '%s'\n",
730 pkg->name, db->treename);
731 db->pkgcache = alpm_list_add(db->pkgcache, pkg);
732 count++;
735 closedir(dbdir);
736 db->pkgcache = alpm_list_msort(db->pkgcache, count, _alpm_pkg_cmp);
737 return(count);
740 /* Note: the return value must be freed by the caller */
741 static char *get_pkgpath(pmdb_t *db, pmpkg_t *info)
743 size_t len;
744 char *pkgpath;
745 const char *dbpath;
747 dbpath = _alpm_db_path(db);
748 len = strlen(dbpath) + strlen(info->name) + strlen(info->version) + 3;
749 MALLOC(pkgpath, len, RET_ERR(PM_ERR_MEMORY, NULL));
750 sprintf(pkgpath, "%s%s-%s/", dbpath, info->name, info->version);
751 return(pkgpath);
754 int _alpm_db_read(pmdb_t *db, pmpkg_t *info, pmdbinfrq_t inforeq)
756 FILE *fp = NULL;
757 char path[PATH_MAX];
758 char line[1024];
759 char *pkgpath = NULL;
761 ALPM_LOG_FUNC;
763 if(db == NULL) {
764 RET_ERR(PM_ERR_DB_NULL, -1);
767 if(info == NULL || info->name == NULL || info->version == NULL) {
768 _alpm_log(PM_LOG_DEBUG, "invalid package entry provided to _alpm_db_read, skipping\n");
769 return(-1);
772 if(info->origin == PKG_FROM_FILE) {
773 _alpm_log(PM_LOG_DEBUG, "request to read database info for a file-based package '%s', skipping...\n", info->name);
774 return(-1);
777 /* bitmask logic here:
778 * infolevel: 00001111
779 * inforeq: 00010100
780 * & result: 00000100
781 * == to inforeq? nope, we need to load more info. */
782 if((info->infolevel & inforeq) == inforeq) {
783 /* already loaded this info, do nothing */
784 return(0);
786 _alpm_log(PM_LOG_FUNCTION, "loading package data for %s : level=0x%x\n",
787 info->name, inforeq);
789 /* clear out 'line', to be certain - and to make valgrind happy */
790 memset(line, 0, sizeof(line));
792 pkgpath = get_pkgpath(db, info);
794 if(access(pkgpath, F_OK)) {
795 /* directory doesn't exist or can't be opened */
796 _alpm_log(PM_LOG_DEBUG, "cannot find '%s-%s' in db '%s'\n",
797 info->name, info->version, db->treename);
798 goto error;
801 /* DESC */
802 if(inforeq & INFRQ_DESC) {
803 snprintf(path, PATH_MAX, "%sdesc", pkgpath);
804 if((fp = fopen(path, "r")) == NULL) {
805 _alpm_log(PM_LOG_ERROR, _("could not open file %s: %s\n"), path, strerror(errno));
806 goto error;
808 while(!feof(fp)) {
809 if(fgets(line, sizeof(line), fp) == NULL) {
810 break;
812 _alpm_strtrim(line);
813 if(strcmp(line, "%NAME%") == 0) {
814 if(fgets(line, sizeof(line), fp) == NULL) {
815 goto error;
817 if(strcmp(_alpm_strtrim(line), info->name) != 0) {
818 _alpm_log(PM_LOG_ERROR, _("%s database is inconsistent: name "
819 "mismatch on package %s\n"), db->treename, info->name);
821 } else if(strcmp(line, "%VERSION%") == 0) {
822 if(fgets(line, sizeof(line), fp) == NULL) {
823 goto error;
825 if(strcmp(_alpm_strtrim(line), info->version) != 0) {
826 _alpm_log(PM_LOG_ERROR, _("%s database is inconsistent: version "
827 "mismatch on package %s\n"), db->treename, info->name);
829 } else if(strcmp(line, "%FILENAME%") == 0) {
830 if(fgets(line, sizeof(line), fp) == NULL) {
831 goto error;
833 STRDUP(info->filename, _alpm_strtrim(line), goto error);
834 } else if(strcmp(line, "%DESC%") == 0) {
835 if(fgets(line, sizeof(line), fp) == NULL) {
836 goto error;
838 STRDUP(info->desc, _alpm_strtrim(line), goto error);
839 } else if(strcmp(line, "%GROUPS%") == 0) {
840 while(fgets(line, sizeof(line), fp) && strlen(_alpm_strtrim(line))) {
841 char *linedup;
842 STRDUP(linedup, _alpm_strtrim(line), goto error);
843 info->groups = alpm_list_add(info->groups, linedup);
845 } else if(strcmp(line, "%URL%") == 0) {
846 if(fgets(line, sizeof(line), fp) == NULL) {
847 goto error;
849 STRDUP(info->url, _alpm_strtrim(line), goto error);
850 } else if(strcmp(line, "%LICENSE%") == 0) {
851 while(fgets(line, sizeof(line), fp) && strlen(_alpm_strtrim(line))) {
852 char *linedup;
853 STRDUP(linedup, _alpm_strtrim(line), goto error);
854 info->licenses = alpm_list_add(info->licenses, linedup);
856 } else if(strcmp(line, "%ARCH%") == 0) {
857 if(fgets(line, sizeof(line), fp) == NULL) {
858 goto error;
860 STRDUP(info->arch, _alpm_strtrim(line), goto error);
861 } else if(strcmp(line, "%BUILDDATE%") == 0) {
862 if(fgets(line, sizeof(line), fp) == NULL) {
863 goto error;
865 _alpm_strtrim(line);
867 char first = tolower((unsigned char)line[0]);
868 if(first > 'a' && first < 'z') {
869 struct tm tmp_tm = {0}; /* initialize to null in case of failure */
870 setlocale(LC_TIME, "C");
871 strptime(line, "%a %b %e %H:%M:%S %Y", &tmp_tm);
872 info->builddate = mktime(&tmp_tm);
873 setlocale(LC_TIME, "");
874 } else {
875 info->builddate = atol(line);
877 } else if(strcmp(line, "%INSTALLDATE%") == 0) {
878 if(fgets(line, sizeof(line), fp) == NULL) {
879 goto error;
881 _alpm_strtrim(line);
883 char first = tolower((unsigned char)line[0]);
884 if(first > 'a' && first < 'z') {
885 struct tm tmp_tm = {0}; /* initialize to null in case of failure */
886 setlocale(LC_TIME, "C");
887 strptime(line, "%a %b %e %H:%M:%S %Y", &tmp_tm);
888 info->installdate = mktime(&tmp_tm);
889 setlocale(LC_TIME, "");
890 } else {
891 info->installdate = atol(line);
893 } else if(strcmp(line, "%PACKAGER%") == 0) {
894 if(fgets(line, sizeof(line), fp) == NULL) {
895 goto error;
897 STRDUP(info->packager, _alpm_strtrim(line), goto error);
898 } else if(strcmp(line, "%REASON%") == 0) {
899 if(fgets(line, sizeof(line), fp) == NULL) {
900 goto error;
902 info->reason = (pmpkgreason_t)atol(_alpm_strtrim(line));
903 } else if(strcmp(line, "%SIZE%") == 0 || strcmp(line, "%CSIZE%") == 0) {
904 /* NOTE: the CSIZE and SIZE fields both share the "size" field
905 * in the pkginfo_t struct. This can be done b/c CSIZE
906 * is currently only used in sync databases, and SIZE is
907 * only used in local databases.
909 if(fgets(line, sizeof(line), fp) == NULL) {
910 goto error;
912 info->size = atol(_alpm_strtrim(line));
913 /* also store this value to isize if isize is unset */
914 if(info->isize == 0) {
915 info->isize = info->size;
917 } else if(strcmp(line, "%ISIZE%") == 0) {
918 /* ISIZE (installed size) tag only appears in sync repositories,
919 * not the local one. */
920 if(fgets(line, sizeof(line), fp) == NULL) {
921 goto error;
923 info->isize = atol(_alpm_strtrim(line));
924 } else if(strcmp(line, "%MD5SUM%") == 0) {
925 /* MD5SUM tag only appears in sync repositories,
926 * not the local one. */
927 if(fgets(line, sizeof(line), fp) == NULL) {
928 goto error;
930 STRDUP(info->md5sum, _alpm_strtrim(line), goto error);
931 } else if(strcmp(line, "%REPLACES%") == 0) {
932 while(fgets(line, sizeof(line), fp) && strlen(_alpm_strtrim(line))) {
933 char *linedup;
934 STRDUP(linedup, _alpm_strtrim(line), goto error);
935 info->replaces = alpm_list_add(info->replaces, linedup);
937 } else if(strcmp(line, "%FORCE%") == 0) {
938 info->force = 1;
941 fclose(fp);
942 fp = NULL;
945 /* FILES */
946 if(inforeq & INFRQ_FILES) {
947 snprintf(path, PATH_MAX, "%sfiles", pkgpath);
948 if((fp = fopen(path, "r")) == NULL) {
949 _alpm_log(PM_LOG_ERROR, _("could not open file %s: %s\n"), path, strerror(errno));
950 goto error;
952 while(fgets(line, sizeof(line), fp)) {
953 _alpm_strtrim(line);
954 if(strcmp(line, "%FILES%") == 0) {
955 while(fgets(line, sizeof(line), fp) && strlen(_alpm_strtrim(line))) {
956 char *linedup;
957 STRDUP(linedup, _alpm_strtrim(line), goto error);
958 info->files = alpm_list_add(info->files, linedup);
960 } else if(strcmp(line, "%BACKUP%") == 0) {
961 while(fgets(line, sizeof(line), fp) && strlen(_alpm_strtrim(line))) {
962 char *linedup;
963 STRDUP(linedup, _alpm_strtrim(line), goto error);
964 info->backup = alpm_list_add(info->backup, linedup);
968 fclose(fp);
969 fp = NULL;
972 /* DEPENDS */
973 if(inforeq & INFRQ_DEPENDS) {
974 snprintf(path, PATH_MAX, "%sdepends", pkgpath);
975 if((fp = fopen(path, "r")) == NULL) {
976 _alpm_log(PM_LOG_ERROR, _("could not open file %s: %s\n"), path, strerror(errno));
977 goto error;
979 while(!feof(fp)) {
980 if(fgets(line, sizeof(line), fp) == NULL) {
981 break;
983 _alpm_strtrim(line);
984 if(strcmp(line, "%DEPENDS%") == 0) {
985 while(fgets(line, sizeof(line), fp) && strlen(_alpm_strtrim(line))) {
986 pmdepend_t *dep = _alpm_splitdep(_alpm_strtrim(line));
987 info->depends = alpm_list_add(info->depends, dep);
989 } else if(strcmp(line, "%OPTDEPENDS%") == 0) {
990 while(fgets(line, sizeof(line), fp) && strlen(_alpm_strtrim(line))) {
991 char *linedup;
992 STRDUP(linedup, _alpm_strtrim(line), goto error);
993 info->optdepends = alpm_list_add(info->optdepends, linedup);
995 } else if(strcmp(line, "%CONFLICTS%") == 0) {
996 while(fgets(line, sizeof(line), fp) && strlen(_alpm_strtrim(line))) {
997 char *linedup;
998 STRDUP(linedup, _alpm_strtrim(line), goto error);
999 info->conflicts = alpm_list_add(info->conflicts, linedup);
1001 } else if(strcmp(line, "%PROVIDES%") == 0) {
1002 while(fgets(line, sizeof(line), fp) && strlen(_alpm_strtrim(line))) {
1003 char *linedup;
1004 STRDUP(linedup, _alpm_strtrim(line), goto error);
1005 info->provides = alpm_list_add(info->provides, linedup);
1009 fclose(fp);
1010 fp = NULL;
1013 /* DELTAS */
1014 if(inforeq & INFRQ_DELTAS) {
1015 snprintf(path, PATH_MAX, "%sdeltas", pkgpath);
1016 if((fp = fopen(path, "r"))) {
1017 while(!feof(fp)) {
1018 if(fgets(line, sizeof(line), fp) == NULL) {
1019 break;
1021 _alpm_strtrim(line);
1022 if(strcmp(line, "%DELTAS%") == 0) {
1023 while(fgets(line, sizeof(line), fp) && strlen(_alpm_strtrim(line))) {
1024 pmdelta_t *delta = _alpm_delta_parse(line);
1025 if(delta) {
1026 info->deltas = alpm_list_add(info->deltas, delta);
1031 fclose(fp);
1032 fp = NULL;
1036 /* INSTALL */
1037 if(inforeq & INFRQ_SCRIPTLET) {
1038 snprintf(path, PATH_MAX, "%sinstall", pkgpath);
1039 if(access(path, F_OK) == 0) {
1040 info->scriptlet = 1;
1044 /* internal */
1045 info->infolevel |= inforeq;
1047 free(pkgpath);
1048 return(0);
1050 error:
1051 free(pkgpath);
1052 if(fp) {
1053 fclose(fp);
1055 return(-1);
1058 int _alpm_db_prepare(pmdb_t *db, pmpkg_t *info)
1060 mode_t oldmask;
1061 int retval = 0;
1062 char *pkgpath = NULL;
1064 if(checkdbdir(db) != 0) {
1065 return(-1);
1068 oldmask = umask(0000);
1069 pkgpath = get_pkgpath(db, info);
1071 if((retval = mkdir(pkgpath, 0755)) != 0) {
1072 _alpm_log(PM_LOG_ERROR, _("could not create directory %s: %s\n"),
1073 pkgpath, strerror(errno));
1076 free(pkgpath);
1077 umask(oldmask);
1079 return(retval);
1082 int _alpm_db_write(pmdb_t *db, pmpkg_t *info, pmdbinfrq_t inforeq)
1084 FILE *fp = NULL;
1085 char path[PATH_MAX];
1086 mode_t oldmask;
1087 alpm_list_t *lp = NULL;
1088 int retval = 0;
1089 int local = 0;
1090 char *pkgpath = NULL;
1092 ALPM_LOG_FUNC;
1094 if(db == NULL || info == NULL) {
1095 return(-1);
1098 pkgpath = get_pkgpath(db, info);
1100 /* make sure we have a sane umask */
1101 oldmask = umask(0022);
1103 if(strcmp(db->treename, "local") == 0) {
1104 local = 1;
1107 /* DESC */
1108 if(inforeq & INFRQ_DESC) {
1109 _alpm_log(PM_LOG_DEBUG, "writing %s-%s DESC information back to db\n",
1110 info->name, info->version);
1111 snprintf(path, PATH_MAX, "%sdesc", pkgpath);
1112 if((fp = fopen(path, "w")) == NULL) {
1113 _alpm_log(PM_LOG_ERROR, _("could not open file %s: %s\n"), path, strerror(errno));
1114 retval = -1;
1115 goto cleanup;
1117 fprintf(fp, "%%NAME%%\n%s\n\n"
1118 "%%VERSION%%\n%s\n\n", info->name, info->version);
1119 if(info->desc) {
1120 fprintf(fp, "%%DESC%%\n"
1121 "%s\n\n", info->desc);
1123 if(info->groups) {
1124 fputs("%GROUPS%\n", fp);
1125 for(lp = info->groups; lp; lp = lp->next) {
1126 fprintf(fp, "%s\n", (char *)lp->data);
1128 fprintf(fp, "\n");
1130 if(info->replaces) {
1131 fputs("%REPLACES%\n", fp);
1132 for(lp = info->replaces; lp; lp = lp->next) {
1133 fprintf(fp, "%s\n", (char *)lp->data);
1135 fprintf(fp, "\n");
1137 if(info->force) {
1138 fprintf(fp, "%%FORCE%%\n\n");
1140 if(local) {
1141 if(info->url) {
1142 fprintf(fp, "%%URL%%\n"
1143 "%s\n\n", info->url);
1145 if(info->licenses) {
1146 fputs("%LICENSE%\n", fp);
1147 for(lp = info->licenses; lp; lp = lp->next) {
1148 fprintf(fp, "%s\n", (char *)lp->data);
1150 fprintf(fp, "\n");
1152 if(info->arch) {
1153 fprintf(fp, "%%ARCH%%\n"
1154 "%s\n\n", info->arch);
1156 if(info->builddate) {
1157 fprintf(fp, "%%BUILDDATE%%\n"
1158 "%ld\n\n", info->builddate);
1160 if(info->installdate) {
1161 fprintf(fp, "%%INSTALLDATE%%\n"
1162 "%ld\n\n", info->installdate);
1164 if(info->packager) {
1165 fprintf(fp, "%%PACKAGER%%\n"
1166 "%s\n\n", info->packager);
1168 if(info->isize) {
1169 /* only write installed size, csize is irrelevant once installed */
1170 fprintf(fp, "%%SIZE%%\n"
1171 "%jd\n\n", (intmax_t)info->isize);
1173 if(info->reason) {
1174 fprintf(fp, "%%REASON%%\n"
1175 "%u\n\n", info->reason);
1177 } else {
1178 if(info->size) {
1179 fprintf(fp, "%%CSIZE%%\n"
1180 "%jd\n\n", (intmax_t)info->size);
1182 if(info->isize) {
1183 fprintf(fp, "%%ISIZE%%\n"
1184 "%jd\n\n", (intmax_t)info->isize);
1186 if(info->md5sum) {
1187 fprintf(fp, "%%MD5SUM%%\n"
1188 "%s\n\n", info->md5sum);
1191 fclose(fp);
1192 fp = NULL;
1195 /* FILES */
1196 if(local && (inforeq & INFRQ_FILES)) {
1197 _alpm_log(PM_LOG_DEBUG, "writing %s-%s FILES information back to db\n",
1198 info->name, info->version);
1199 snprintf(path, PATH_MAX, "%sfiles", pkgpath);
1200 if((fp = fopen(path, "w")) == NULL) {
1201 _alpm_log(PM_LOG_ERROR, _("could not open file %s: %s\n"), path, strerror(errno));
1202 retval = -1;
1203 goto cleanup;
1205 if(info->files) {
1206 fprintf(fp, "%%FILES%%\n");
1207 for(lp = info->files; lp; lp = lp->next) {
1208 fprintf(fp, "%s\n", (char *)lp->data);
1210 fprintf(fp, "\n");
1212 if(info->backup) {
1213 fprintf(fp, "%%BACKUP%%\n");
1214 for(lp = info->backup; lp; lp = lp->next) {
1215 fprintf(fp, "%s\n", (char *)lp->data);
1217 fprintf(fp, "\n");
1219 fclose(fp);
1220 fp = NULL;
1223 /* DEPENDS */
1224 if(inforeq & INFRQ_DEPENDS) {
1225 _alpm_log(PM_LOG_DEBUG, "writing %s-%s DEPENDS information back to db\n",
1226 info->name, info->version);
1227 snprintf(path, PATH_MAX, "%sdepends", pkgpath);
1228 if((fp = fopen(path, "w")) == NULL) {
1229 _alpm_log(PM_LOG_ERROR, _("could not open file %s: %s\n"), path, strerror(errno));
1230 retval = -1;
1231 goto cleanup;
1233 if(info->depends) {
1234 fputs("%DEPENDS%\n", fp);
1235 for(lp = info->depends; lp; lp = lp->next) {
1236 char *depstring = alpm_dep_compute_string(lp->data);
1237 fprintf(fp, "%s\n", depstring);
1238 free(depstring);
1240 fprintf(fp, "\n");
1242 if(info->optdepends) {
1243 fputs("%OPTDEPENDS%\n", fp);
1244 for(lp = info->optdepends; lp; lp = lp->next) {
1245 fprintf(fp, "%s\n", (char *)lp->data);
1247 fprintf(fp, "\n");
1249 if(info->conflicts) {
1250 fputs("%CONFLICTS%\n", fp);
1251 for(lp = info->conflicts; lp; lp = lp->next) {
1252 fprintf(fp, "%s\n", (char *)lp->data);
1254 fprintf(fp, "\n");
1256 if(info->provides) {
1257 fputs("%PROVIDES%\n", fp);
1258 for(lp = info->provides; lp; lp = lp->next) {
1259 fprintf(fp, "%s\n", (char *)lp->data);
1261 fprintf(fp, "\n");
1263 fclose(fp);
1264 fp = NULL;
1267 /* INSTALL */
1268 /* nothing needed here (script is automatically extracted) */
1270 cleanup:
1271 umask(oldmask);
1272 free(pkgpath);
1274 if(fp) {
1275 fclose(fp);
1278 return(retval);
1281 int _alpm_db_remove(pmdb_t *db, pmpkg_t *info)
1283 int ret = 0;
1284 char *pkgpath = NULL;
1286 ALPM_LOG_FUNC;
1288 if(db == NULL || info == NULL) {
1289 RET_ERR(PM_ERR_DB_NULL, -1);
1292 pkgpath = get_pkgpath(db, info);
1294 ret = _alpm_rmrf(pkgpath);
1295 free(pkgpath);
1296 if(ret != 0) {
1297 ret = -1;
1299 return(ret);
1302 /* vim: set ts=2 sw=2 noet: */