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/>.
34 #include "alpm_list.h"
42 /* global handle variable */
43 extern pmhandle_t
*handle
;
45 /** \addtogroup alpm_databases Database Functions
46 * @brief Functions to query and manipulate the database of libalpm
50 /** Register a sync database of packages. */
51 pmdb_t SYMEXPORT
*alpm_db_register_sync(const char *treename
)
54 ASSERT(handle
!= NULL
, RET_ERR(PM_ERR_HANDLE_NULL
, NULL
));
55 ASSERT(treename
!= NULL
&& strlen(treename
) != 0, RET_ERR(PM_ERR_WRONG_ARGS
, NULL
));
56 /* Do not register a database if a transaction is on-going */
57 ASSERT(handle
->trans
== NULL
, RET_ERR(PM_ERR_TRANS_NOT_NULL
, NULL
));
59 return _alpm_db_register_sync(treename
);
62 /* Helper function for alpm_db_unregister{_all} */
63 void _alpm_db_unregister(pmdb_t
*db
)
69 _alpm_log(PM_LOG_DEBUG
, "unregistering database '%s'\n", db
->treename
);
73 /** Unregister all package databases. */
74 int SYMEXPORT
alpm_db_unregister_all(void)
80 ASSERT(handle
!= NULL
, RET_ERR(PM_ERR_HANDLE_NULL
, -1));
81 /* Do not unregister a database if a transaction is on-going */
82 ASSERT(handle
->trans
== NULL
, RET_ERR(PM_ERR_TRANS_NOT_NULL
, -1));
84 /* unregister all sync dbs */
85 for(i
= handle
->dbs_sync
; i
; i
= i
->next
) {
87 db
->ops
->unregister(db
);
90 FREELIST(handle
->dbs_sync
);
94 /** Unregister a package database. */
95 int SYMEXPORT
alpm_db_unregister(pmdb_t
*db
)
100 ASSERT(handle
!= NULL
, RET_ERR(PM_ERR_HANDLE_NULL
, -1));
101 ASSERT(db
!= NULL
, RET_ERR(PM_ERR_WRONG_ARGS
, -1));
102 /* Do not unregister a database if a transaction is on-going */
103 ASSERT(handle
->trans
== NULL
, RET_ERR(PM_ERR_TRANS_NOT_NULL
, -1));
105 if(db
== handle
->db_local
) {
106 handle
->db_local
= NULL
;
109 /* Warning : this function shouldn't be used to unregister all sync
110 * databases by walking through the list returned by
111 * alpm_option_get_syncdbs, because the db is removed from that list here.
114 handle
->dbs_sync
= alpm_list_remove(handle
->dbs_sync
,
115 db
, _alpm_db_cmp
, &data
);
122 RET_ERR(PM_ERR_DB_NOT_FOUND
, -1);
125 db
->ops
->unregister(db
);
129 /** Get the serverlist of a database. */
130 alpm_list_t SYMEXPORT
*alpm_db_get_servers(const pmdb_t
*db
)
133 ASSERT(db
!= NULL
, RET_ERR(PM_ERR_DB_NULL
, NULL
));
138 /** Set the serverlist of a database. */
139 int SYMEXPORT
alpm_db_set_servers(pmdb_t
*db
, alpm_list_t
*servers
)
142 ASSERT(db
!= NULL
, RET_ERR(PM_ERR_DB_NULL
, -1));
144 if(db
->servers
) FREELIST(db
->servers
);
145 db
->servers
= servers
;
149 static char *sanitize_url(const char *url
)
152 size_t len
= strlen(url
);
154 STRDUP(newurl
, url
, RET_ERR(PM_ERR_MEMORY
, NULL
));
155 /* strip the trailing slash if one exists */
156 if(newurl
[len
- 1] == '/') {
157 newurl
[len
- 1] = '\0';
162 /** Add a download server to a database.
163 * @param db database pointer
164 * @param url url of the server
165 * @return 0 on success, -1 on error (pm_errno is set accordingly)
167 int SYMEXPORT
alpm_db_add_server(pmdb_t
*db
, const char *url
)
172 ASSERT(db
!= NULL
, RET_ERR(PM_ERR_DB_NULL
, -1));
173 ASSERT(url
!= NULL
&& strlen(url
) != 0, RET_ERR(PM_ERR_WRONG_ARGS
, -1));
175 newurl
= sanitize_url(url
);
179 db
->servers
= alpm_list_add(db
->servers
, newurl
);
180 _alpm_log(PM_LOG_DEBUG
, "adding new server URL to database '%s': %s\n",
181 db
->treename
, newurl
);
186 /** Remove a download server from a database.
187 * @param db database pointer
188 * @param url url of the server
189 * @return 0 on success, 1 on server not present,
190 * -1 on error (pm_errno is set accordingly)
192 int SYMEXPORT
alpm_db_remove_server(pmdb_t
*db
, const char *url
)
194 char *newurl
, *vdata
= NULL
;
197 ASSERT(db
!= NULL
, RET_ERR(PM_ERR_DB_NULL
, -1));
198 ASSERT(url
!= NULL
&& strlen(url
) != 0, RET_ERR(PM_ERR_WRONG_ARGS
, -1));
200 newurl
= sanitize_url(url
);
204 db
->servers
= alpm_list_remove_str(db
->servers
, newurl
, &vdata
);
207 _alpm_log(PM_LOG_DEBUG
, "removed server URL from database '%s': %s\n",
208 db
->treename
, newurl
);
215 /** Set the verify gpg signature option for a database.
216 * @param db database pointer
217 * @param verify enum pgp_verify_t
218 * @return 0 on success, -1 on error (pm_errno is set accordingly)
220 int SYMEXPORT
alpm_db_set_pgp_verify(pmdb_t
*db
, pgp_verify_t verify
)
223 ASSERT(db
!= NULL
, RET_ERR(PM_ERR_DB_NULL
, -1));
225 db
->pgp_verify
= verify
;
226 _alpm_log(PM_LOG_DEBUG
, "adding VerifySig option to database '%s': %d\n",
227 db
->treename
, verify
);
232 /** Get the name of a package database. */
233 const char SYMEXPORT
*alpm_db_get_name(const pmdb_t
*db
)
236 ASSERT(db
!= NULL
, return NULL
);
241 /** Get a download URL for the package database. */
242 const char SYMEXPORT
*alpm_db_get_url(const pmdb_t
*db
)
247 ASSERT(db
!= NULL
, return NULL
);
248 ASSERT(db
->servers
!= NULL
, return NULL
);
250 url
= (char *)db
->servers
->data
;
256 /** Get a package entry from a package database. */
257 pmpkg_t SYMEXPORT
*alpm_db_get_pkg(pmdb_t
*db
, const char *name
)
260 ASSERT(db
!= NULL
, return NULL
);
261 ASSERT(name
!= NULL
&& strlen(name
) != 0, return NULL
);
263 return _alpm_db_get_pkgfromcache(db
, name
);
266 /** Get the package cache of a package database. */
267 alpm_list_t SYMEXPORT
*alpm_db_get_pkgcache(pmdb_t
*db
)
270 ASSERT(db
!= NULL
, return NULL
);
272 return _alpm_db_get_pkgcache(db
);
275 /** Get a group entry from a package database. */
276 pmgrp_t SYMEXPORT
*alpm_db_readgrp(pmdb_t
*db
, const char *name
)
279 ASSERT(db
!= NULL
, return NULL
);
280 ASSERT(name
!= NULL
&& strlen(name
) != 0, return NULL
);
282 return _alpm_db_get_grpfromcache(db
, name
);
285 /** Get the group cache of a package database. */
286 alpm_list_t SYMEXPORT
*alpm_db_get_grpcache(pmdb_t
*db
)
289 ASSERT(db
!= NULL
, return NULL
);
291 return _alpm_db_get_grpcache(db
);
294 /** Searches a database. */
295 alpm_list_t SYMEXPORT
*alpm_db_search(pmdb_t
*db
, const alpm_list_t
* needles
)
298 ASSERT(db
!= NULL
, return NULL
);
300 return _alpm_db_search(db
, needles
);
303 /** Set install reason for a package in db. */
304 int SYMEXPORT
alpm_db_set_pkgreason(pmdb_t
*db
, const char *name
, pmpkgreason_t reason
)
307 ASSERT(db
!= NULL
&& name
!= NULL
, RET_ERR(PM_ERR_WRONG_ARGS
, -1));
309 pmpkg_t
*pkg
= _alpm_db_get_pkgfromcache(db
, name
);
311 RET_ERR(PM_ERR_PKG_NOT_FOUND
, -1);
314 _alpm_log(PM_LOG_DEBUG
, "setting install reason %u for %s/%s\n", reason
, db
->treename
, name
);
315 if(alpm_pkg_get_reason(pkg
) == reason
) {
319 /* set reason (in pkgcache) */
320 pkg
->reason
= reason
;
322 if(_alpm_local_db_write(db
, pkg
, INFRQ_DESC
)) {
323 RET_ERR(PM_ERR_DB_WRITE
, -1);
331 pmdb_t
*_alpm_db_new(const char *treename
, int is_local
)
335 CALLOC(db
, 1, sizeof(pmdb_t
), RET_ERR(PM_ERR_MEMORY
, NULL
));
336 STRDUP(db
->treename
, treename
, RET_ERR(PM_ERR_MEMORY
, NULL
));
337 db
->is_local
= is_local
;
342 void _alpm_db_free(pmdb_t
*db
)
344 /* cleanup pkgcache */
345 _alpm_db_free_pkgcache(db
);
346 /* cleanup server list */
347 FREELIST(db
->servers
);
355 const char *_alpm_db_path(pmdb_t
*db
)
364 dbpath
= alpm_option_get_dbpath();
366 _alpm_log(PM_LOG_ERROR
, _("database path is undefined\n"));
367 RET_ERR(PM_ERR_DB_OPEN
, NULL
);
371 pathsize
= strlen(dbpath
) + strlen(db
->treename
) + 2;
372 CALLOC(db
->_path
, 1, pathsize
, RET_ERR(PM_ERR_MEMORY
, NULL
));
373 sprintf(db
->_path
, "%s%s/", dbpath
, db
->treename
);
375 pathsize
= strlen(dbpath
) + 5 + strlen(db
->treename
) + 4;
376 CALLOC(db
->_path
, 1, pathsize
, RET_ERR(PM_ERR_MEMORY
, NULL
));
377 /* all sync DBs now reside in the sync/ subdir of the dbpath */
378 sprintf(db
->_path
, "%ssync/%s.db", dbpath
, db
->treename
);
380 _alpm_log(PM_LOG_DEBUG
, "database path for tree %s set to %s\n",
381 db
->treename
, db
->_path
);
386 int _alpm_db_version(pmdb_t
*db
)
391 return db
->ops
->version(db
);
394 int _alpm_db_cmp(const void *d1
, const void *d2
)
396 pmdb_t
*db1
= (pmdb_t
*)d1
;
397 pmdb_t
*db2
= (pmdb_t
*)d2
;
398 return strcmp(db1
->treename
, db2
->treename
);
401 alpm_list_t
*_alpm_db_search(pmdb_t
*db
, const alpm_list_t
*needles
)
403 const alpm_list_t
*i
, *j
, *k
;
404 alpm_list_t
*ret
= NULL
;
405 /* copy the pkgcache- we will free the list var after each needle */
406 alpm_list_t
*list
= alpm_list_copy(_alpm_db_get_pkgcache(db
));
408 for(i
= needles
; i
; i
= i
->next
) {
412 if(i
->data
== NULL
) {
417 _alpm_log(PM_LOG_DEBUG
, "searching for target '%s'\n", targ
);
419 if(regcomp(®
, targ
, REG_EXTENDED
| REG_NOSUB
| REG_ICASE
| REG_NEWLINE
) != 0) {
420 RET_ERR(PM_ERR_INVALID_REGEX
, NULL
);
423 for(j
= list
; j
; j
= j
->next
) {
424 pmpkg_t
*pkg
= j
->data
;
425 const char *matched
= NULL
;
426 const char *name
= alpm_pkg_get_name(pkg
);
427 const char *desc
= alpm_pkg_get_desc(pkg
);
429 /* check name as regex AND as plain text */
430 if(name
&& (regexec(®
, name
, 0, 0, 0) == 0 || strstr(name
, targ
))) {
434 else if(desc
&& regexec(®
, desc
, 0, 0, 0) == 0) {
437 /* TODO: should we be doing this, and should we print something
438 * differently when we do match it since it isn't currently printed? */
441 for(k
= alpm_pkg_get_provides(pkg
); k
; k
= k
->next
) {
442 if(regexec(®
, k
->data
, 0, 0, 0) == 0) {
450 for(k
= alpm_pkg_get_groups(pkg
); k
; k
= k
->next
) {
451 if(regexec(®
, k
->data
, 0, 0, 0) == 0) {
458 if(matched
!= NULL
) {
459 _alpm_log(PM_LOG_DEBUG
, " search target '%s' matched '%s'\n",
461 ret
= alpm_list_add(ret
, pkg
);
465 /* Free the existing search list, and use the returned list for the
466 * next needle. This allows for AND-based package searching. */
467 alpm_list_free(list
);
475 /* Returns a new package cache from db.
476 * It frees the cache if it already exists.
478 int _alpm_db_load_pkgcache(pmdb_t
*db
)
483 _alpm_db_free_pkgcache(db
);
485 _alpm_log(PM_LOG_DEBUG
, "loading package cache for repository '%s'\n",
487 if(db
->ops
->populate(db
) == -1) {
488 _alpm_log(PM_LOG_DEBUG
,
489 "failed to load package cache for repository '%s'\n", db
->treename
);
493 db
->pkgcache_loaded
= 1;
497 void _alpm_db_free_pkgcache(pmdb_t
*db
)
499 if(db
== NULL
|| !db
->pkgcache_loaded
) {
503 _alpm_log(PM_LOG_DEBUG
, "freeing package cache for repository '%s'\n",
506 alpm_list_free_inner(_alpm_db_get_pkgcache(db
),
507 (alpm_list_fn_free
)_alpm_pkg_free
);
508 _alpm_pkghash_free(db
->pkgcache
);
509 db
->pkgcache_loaded
= 0;
511 _alpm_db_free_grpcache(db
);
514 pmpkghash_t
*_alpm_db_get_pkgcache_hash(pmdb_t
*db
)
520 if(!db
->pkgcache_loaded
) {
521 _alpm_db_load_pkgcache(db
);
524 /* hmmm, still NULL ?*/
526 _alpm_log(PM_LOG_DEBUG
, "warning: pkgcache is NULL for db '%s'\n", db
->treename
);
532 alpm_list_t
*_alpm_db_get_pkgcache(pmdb_t
*db
)
534 pmpkghash_t
*hash
= _alpm_db_get_pkgcache_hash(db
);
543 /* "duplicate" pkg then add it to pkgcache */
544 int _alpm_db_add_pkgincache(pmdb_t
*db
, pmpkg_t
*pkg
)
548 if(db
== NULL
|| !db
->pkgcache_loaded
|| pkg
== NULL
) {
552 newpkg
= _alpm_pkg_dup(pkg
);
557 _alpm_log(PM_LOG_DEBUG
, "adding entry '%s' in '%s' cache\n",
558 alpm_pkg_get_name(newpkg
), db
->treename
);
559 db
->pkgcache
= _alpm_pkghash_add_sorted(db
->pkgcache
, newpkg
);
561 _alpm_db_free_grpcache(db
);
566 int _alpm_db_remove_pkgfromcache(pmdb_t
*db
, pmpkg_t
*pkg
)
568 pmpkg_t
*data
= NULL
;
570 if(db
== NULL
|| !db
->pkgcache_loaded
|| pkg
== NULL
) {
574 _alpm_log(PM_LOG_DEBUG
, "removing entry '%s' from '%s' cache\n",
575 alpm_pkg_get_name(pkg
), db
->treename
);
577 db
->pkgcache
= _alpm_pkghash_remove(db
->pkgcache
, pkg
, &data
);
579 /* package not found */
580 _alpm_log(PM_LOG_DEBUG
, "cannot remove entry '%s' from '%s' cache: not found\n",
581 alpm_pkg_get_name(pkg
), db
->treename
);
585 _alpm_pkg_free(data
);
587 _alpm_db_free_grpcache(db
);
592 pmpkg_t
*_alpm_db_get_pkgfromcache(pmdb_t
*db
, const char *target
)
598 pmpkghash_t
*pkgcache
= _alpm_db_get_pkgcache_hash(db
);
600 _alpm_log(PM_LOG_DEBUG
, "warning: failed to get '%s' from NULL pkgcache\n",
605 return _alpm_pkghash_find(pkgcache
, target
);
608 /* Returns a new group cache from db.
610 int _alpm_db_load_grpcache(pmdb_t
*db
)
618 _alpm_log(PM_LOG_DEBUG
, "loading group cache for repository '%s'\n",
621 for(lp
= _alpm_db_get_pkgcache(db
); lp
; lp
= lp
->next
) {
622 const alpm_list_t
*i
;
623 pmpkg_t
*pkg
= lp
->data
;
625 for(i
= alpm_pkg_get_groups(pkg
); i
; i
= i
->next
) {
626 const char *grpname
= i
->data
;
631 /* first look through the group cache for a group with this name */
632 for(j
= db
->grpcache
; j
; j
= j
->next
) {
635 if(strcmp(grp
->name
, grpname
) == 0
636 && !alpm_list_find_ptr(grp
->packages
, pkg
)) {
637 grp
->packages
= alpm_list_add(grp
->packages
, pkg
);
645 /* we didn't find the group, so create a new one with this name */
646 grp
= _alpm_grp_new(grpname
);
647 grp
->packages
= alpm_list_add(grp
->packages
, pkg
);
648 db
->grpcache
= alpm_list_add(db
->grpcache
, grp
);
652 db
->grpcache_loaded
= 1;
656 void _alpm_db_free_grpcache(pmdb_t
*db
)
660 if(db
== NULL
|| !db
->grpcache_loaded
) {
664 _alpm_log(PM_LOG_DEBUG
, "freeing group cache for repository '%s'\n",
667 for(lg
= db
->grpcache
; lg
; lg
= lg
->next
) {
668 _alpm_grp_free(lg
->data
);
671 FREELIST(db
->grpcache
);
672 db
->grpcache_loaded
= 0;
675 alpm_list_t
*_alpm_db_get_grpcache(pmdb_t
*db
)
681 if(!db
->grpcache_loaded
) {
682 _alpm_db_load_grpcache(db
);
688 pmgrp_t
*_alpm_db_get_grpfromcache(pmdb_t
*db
, const char *target
)
692 if(db
== NULL
|| target
== NULL
|| strlen(target
) == 0) {
696 for(i
= _alpm_db_get_grpcache(db
); i
; i
= i
->next
) {
697 pmgrp_t
*info
= i
->data
;
699 if(strcmp(info
->name
, target
) == 0) {
707 /* vim: set ts=2 sw=2 noet: */