1 /*
2  *  db.c
3  *
4  *  Copyright (c) 2006-2018 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>
10  *
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.
15  *
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.
20  *
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/>.
23  */
24 
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <regex.h>
29 
30 /* libalpm */
31 #include "db.h"
32 #include "alpm_list.h"
33 #include "log.h"
34 #include "util.h"
35 #include "handle.h"
36 #include "alpm.h"
37 #include "package.h"
38 #include "group.h"
39 
40 /** \addtogroup alpm_databases Database Functions
41  * @brief Functions to query and manipulate the database of libalpm
42  * @{
43  */
44 
45 /** Register a sync database of packages. */
alpm_register_syncdb(alpm_handle_t * handle,const char * treename,int siglevel)46 alpm_db_t SYMEXPORT *alpm_register_syncdb(alpm_handle_t *handle,
47 		const char *treename, int siglevel)
48 {
49 	alpm_list_t *i;
50 
51 	/* Sanity checks */
52 	CHECK_HANDLE(handle, return NULL);
53 	ASSERT(treename != NULL && strlen(treename) != 0,
54 			RET_ERR(handle, ALPM_ERR_WRONG_ARGS, NULL));
55 	ASSERT(!strchr(treename, '/'), RET_ERR(handle, ALPM_ERR_WRONG_ARGS, NULL));
56 	/* Do not register a database if a transaction is on-going */
57 	ASSERT(handle->trans == NULL, RET_ERR(handle, ALPM_ERR_TRANS_NOT_NULL, NULL));
58 
59 	/* ensure database name is unique */
60 	if(strcmp(treename, "local") == 0) {
61 		RET_ERR(handle, ALPM_ERR_DB_NOT_NULL, NULL);
62 	}
63 	for(i = handle->dbs_sync; i; i = i->next) {
64 		alpm_db_t *d = i->data;
65 		if(strcmp(treename, d->treename) == 0) {
66 			RET_ERR(handle, ALPM_ERR_DB_NOT_NULL, NULL);
67 		}
68 	}
69 
70 	return _alpm_db_register_sync(handle, treename, siglevel);
71 }
72 
73 /* Helper function for alpm_db_unregister{_all} */
_alpm_db_unregister(alpm_db_t * db)74 void _alpm_db_unregister(alpm_db_t *db)
75 {
76 	if(db == NULL) {
77 		return;
78 	}
79 
80 	_alpm_log(db->handle, ALPM_LOG_DEBUG, "unregistering database '%s'\n", db->treename);
81 	_alpm_db_free(db);
82 }
83 
84 /** Unregister all package databases. */
alpm_unregister_all_syncdbs(alpm_handle_t * handle)85 int SYMEXPORT alpm_unregister_all_syncdbs(alpm_handle_t *handle)
86 {
87 	alpm_list_t *i;
88 	alpm_db_t *db;
89 
90 	/* Sanity checks */
91 	CHECK_HANDLE(handle, return -1);
92 	/* Do not unregister a database if a transaction is on-going */
93 	ASSERT(handle->trans == NULL, RET_ERR(handle, ALPM_ERR_TRANS_NOT_NULL, -1));
94 
95 	/* unregister all sync dbs */
96 	for(i = handle->dbs_sync; i; i = i->next) {
97 		db = i->data;
98 		db->ops->unregister(db);
99 		i->data = NULL;
100 	}
101 	FREELIST(handle->dbs_sync);
102 	return 0;
103 }
104 
105 /** Unregister a package database. */
alpm_db_unregister(alpm_db_t * db)106 int SYMEXPORT alpm_db_unregister(alpm_db_t *db)
107 {
108 	int found = 0;
109 	alpm_handle_t *handle;
110 
111 	/* Sanity checks */
112 	ASSERT(db != NULL, return -1);
113 	/* Do not unregister a database if a transaction is on-going */
114 	handle = db->handle;
115 	handle->pm_errno = ALPM_ERR_OK;
116 	ASSERT(handle->trans == NULL, RET_ERR(handle, ALPM_ERR_TRANS_NOT_NULL, -1));
117 
118 	if(db == handle->db_local) {
119 		handle->db_local = NULL;
120 		found = 1;
121 	} else {
122 		/* Warning : this function shouldn't be used to unregister all sync
123 		 * databases by walking through the list returned by
124 		 * alpm_get_syncdbs, because the db is removed from that list here.
125 		 */
126 		void *data;
127 		handle->dbs_sync = alpm_list_remove(handle->dbs_sync,
128 				db, _alpm_db_cmp, &data);
129 		if(data) {
130 			found = 1;
131 		}
132 	}
133 
134 	if(!found) {
135 		RET_ERR(handle, ALPM_ERR_DB_NOT_FOUND, -1);
136 	}
137 
138 	db->ops->unregister(db);
139 	return 0;
140 }
141 
142 /** Get the serverlist of a database. */
alpm_db_get_servers(const alpm_db_t * db)143 alpm_list_t SYMEXPORT *alpm_db_get_servers(const alpm_db_t *db)
144 {
145 	ASSERT(db != NULL, return NULL);
146 	return db->servers;
147 }
148 
149 /** Set the serverlist of a database. */
alpm_db_set_servers(alpm_db_t * db,alpm_list_t * servers)150 int SYMEXPORT alpm_db_set_servers(alpm_db_t *db, alpm_list_t *servers)
151 {
152 	ASSERT(db != NULL, return -1);
153 	FREELIST(db->servers);
154 	db->servers = servers;
155 	return 0;
156 }
157 
sanitize_url(const char * url)158 static char *sanitize_url(const char *url)
159 {
160 	char *newurl;
161 	size_t len = strlen(url);
162 
163 	STRDUP(newurl, url, return NULL);
164 	/* strip the trailing slash if one exists */
165 	if(newurl[len - 1] == '/') {
166 		newurl[len - 1] = '\0';
167 	}
168 	return newurl;
169 }
170 
171 /** Add a download server to a database.
172  * @param db database pointer
173  * @param url url of the server
174  * @return 0 on success, -1 on error (pm_errno is set accordingly)
175  */
alpm_db_add_server(alpm_db_t * db,const char * url)176 int SYMEXPORT alpm_db_add_server(alpm_db_t *db, const char *url)
177 {
178 	char *newurl;
179 
180 	/* Sanity checks */
181 	ASSERT(db != NULL, return -1);
182 	db->handle->pm_errno = ALPM_ERR_OK;
183 	ASSERT(url != NULL && strlen(url) != 0, RET_ERR(db->handle, ALPM_ERR_WRONG_ARGS, -1));
184 
185 	newurl = sanitize_url(url);
186 	if(!newurl) {
187 		return -1;
188 	}
189 	db->servers = alpm_list_add(db->servers, newurl);
190 	_alpm_log(db->handle, ALPM_LOG_DEBUG, "adding new server URL to database '%s': %s\n",
191 			db->treename, newurl);
192 
193 	return 0;
194 }
195 
196 /** Remove a download server from a database.
197  * @param db database pointer
198  * @param url url of the server
199  * @return 0 on success, 1 on server not present,
200  * -1 on error (pm_errno is set accordingly)
201  */
alpm_db_remove_server(alpm_db_t * db,const char * url)202 int SYMEXPORT alpm_db_remove_server(alpm_db_t *db, const char *url)
203 {
204 	char *newurl, *vdata = NULL;
205 	int ret = 1;
206 
207 	/* Sanity checks */
208 	ASSERT(db != NULL, return -1);
209 	db->handle->pm_errno = ALPM_ERR_OK;
210 	ASSERT(url != NULL && strlen(url) != 0, RET_ERR(db->handle, ALPM_ERR_WRONG_ARGS, -1));
211 
212 	newurl = sanitize_url(url);
213 	if(!newurl) {
214 		return -1;
215 	}
216 
217 	db->servers = alpm_list_remove_str(db->servers, newurl, &vdata);
218 
219 	if(vdata) {
220 		_alpm_log(db->handle, ALPM_LOG_DEBUG, "removed server URL from database '%s': %s\n",
221 				db->treename, newurl);
222 		free(vdata);
223 		ret = 0;
224 	}
225 
226 	free(newurl);
227 	return ret;
228 }
229 
230 /** Get the name of a package database. */
alpm_db_get_name(const alpm_db_t * db)231 const char SYMEXPORT *alpm_db_get_name(const alpm_db_t *db)
232 {
233 	ASSERT(db != NULL, return NULL);
234 	return db->treename;
235 }
236 
237 /** Get the signature verification level for a database. */
alpm_db_get_siglevel(alpm_db_t * db)238 int SYMEXPORT alpm_db_get_siglevel(alpm_db_t *db)
239 {
240 	ASSERT(db != NULL, return -1);
241 	if(db->siglevel & ALPM_SIG_USE_DEFAULT) {
242 		return db->handle->siglevel;
243 	} else {
244 		return db->siglevel;
245 	}
246 }
247 
248 /** Check the validity of a database. */
alpm_db_get_valid(alpm_db_t * db)249 int SYMEXPORT alpm_db_get_valid(alpm_db_t *db)
250 {
251 	ASSERT(db != NULL, return -1);
252 	db->handle->pm_errno = ALPM_ERR_OK;
253 	return db->ops->validate(db);
254 }
255 
256 /** Get a package entry from a package database. */
alpm_db_get_pkg(alpm_db_t * db,const char * name)257 alpm_pkg_t SYMEXPORT *alpm_db_get_pkg(alpm_db_t *db, const char *name)
258 {
259 	alpm_pkg_t *pkg;
260 	ASSERT(db != NULL, return NULL);
261 	db->handle->pm_errno = ALPM_ERR_OK;
262 	ASSERT(name != NULL && strlen(name) != 0,
263 			RET_ERR(db->handle, ALPM_ERR_WRONG_ARGS, NULL));
264 
265 	pkg = _alpm_db_get_pkgfromcache(db, name);
266 	if(!pkg) {
267 		RET_ERR(db->handle, ALPM_ERR_PKG_NOT_FOUND, NULL);
268 	}
269 	return pkg;
270 }
271 
272 /** Get the package cache of a package database. */
alpm_db_get_pkgcache(alpm_db_t * db)273 alpm_list_t SYMEXPORT *alpm_db_get_pkgcache(alpm_db_t *db)
274 {
275 	ASSERT(db != NULL, return NULL);
276 	db->handle->pm_errno = ALPM_ERR_OK;
277 	return _alpm_db_get_pkgcache(db);
278 }
279 
280 /** Get a group entry from a package database. */
alpm_db_get_group(alpm_db_t * db,const char * name)281 alpm_group_t SYMEXPORT *alpm_db_get_group(alpm_db_t *db, const char *name)
282 {
283 	ASSERT(db != NULL, return NULL);
284 	db->handle->pm_errno = 0;
285 	ASSERT(name != NULL && strlen(name) != 0,
286 			RET_ERR(db->handle, ALPM_ERR_WRONG_ARGS, NULL));
287 
288 	return _alpm_db_get_groupfromcache(db, name);
289 }
290 
291 /** Get the group cache of a package database. */
alpm_db_get_groupcache(alpm_db_t * db)292 alpm_list_t SYMEXPORT *alpm_db_get_groupcache(alpm_db_t *db)
293 {
294 	ASSERT(db != NULL, return NULL);
295 	db->handle->pm_errno = ALPM_ERR_OK;
296 
297 	return _alpm_db_get_groupcache(db);
298 }
299 
300 /** Searches a database. */
alpm_db_search(alpm_db_t * db,const alpm_list_t * needles)301 alpm_list_t SYMEXPORT *alpm_db_search(alpm_db_t *db, const alpm_list_t *needles)
302 {
303 	ASSERT(db != NULL, return NULL);
304 	db->handle->pm_errno = ALPM_ERR_OK;
305 
306 	return _alpm_db_search(db, needles);
307 }
308 
309 /** Sets the usage bitmask for a repo */
alpm_db_set_usage(alpm_db_t * db,int usage)310 int SYMEXPORT alpm_db_set_usage(alpm_db_t *db, int usage)
311 {
312 	ASSERT(db != NULL, return -1);
313 	db->usage = usage;
314 	return 0;
315 }
316 
317 /** Gets the usage bitmask for a repo */
alpm_db_get_usage(alpm_db_t * db,int * usage)318 int SYMEXPORT alpm_db_get_usage(alpm_db_t *db, int *usage)
319 {
320 	ASSERT(db != NULL, return -1);
321 	ASSERT(usage != NULL, return -1);
322 	*usage = db->usage;
323 	return 0;
324 }
325 
326 
327 /** @} */
328 
_alpm_db_new(const char * treename,int is_local)329 alpm_db_t *_alpm_db_new(const char *treename, int is_local)
330 {
331 	alpm_db_t *db;
332 
333 	CALLOC(db, 1, sizeof(alpm_db_t), return NULL);
334 	STRDUP(db->treename, treename, FREE(db); return NULL);
335 	if(is_local) {
336 		db->status |= DB_STATUS_LOCAL;
337 	} else {
338 		db->status &= ~DB_STATUS_LOCAL;
339 	}
340 	db->usage = ALPM_DB_USAGE_ALL;
341 
342 	return db;
343 }
344 
_alpm_db_free(alpm_db_t * db)345 void _alpm_db_free(alpm_db_t *db)
346 {
347 	ASSERT(db != NULL, return);
348 	/* cleanup pkgcache */
349 	_alpm_db_free_pkgcache(db);
350 	/* cleanup server list */
351 	FREELIST(db->servers);
352 	FREE(db->_path);
353 	FREE(db->treename);
354 	FREE(db);
355 
356 	return;
357 }
358 
_alpm_db_path(alpm_db_t * db)359 const char *_alpm_db_path(alpm_db_t *db)
360 {
361 	if(!db) {
362 		return NULL;
363 	}
364 	if(!db->_path) {
365 		const char *dbpath;
366 		size_t pathsize;
367 
368 		dbpath = db->handle->dbpath;
369 		if(!dbpath) {
370 			_alpm_log(db->handle, ALPM_LOG_ERROR, _("database path is undefined\n"));
371 			RET_ERR(db->handle, ALPM_ERR_DB_OPEN, NULL);
372 		}
373 
374 		if(db->status & DB_STATUS_LOCAL) {
375 			pathsize = strlen(dbpath) + strlen(db->treename) + 2;
376 			CALLOC(db->_path, 1, pathsize, RET_ERR(db->handle, ALPM_ERR_MEMORY, NULL));
377 			sprintf(db->_path, "%s%s/", dbpath, db->treename);
378 		} else {
379 			const char *dbext = db->handle->dbext;
380 
381 			pathsize = strlen(dbpath) + 5 + strlen(db->treename) + strlen(dbext) + 1;
382 			CALLOC(db->_path, 1, pathsize, RET_ERR(db->handle, ALPM_ERR_MEMORY, NULL));
383 			/* all sync DBs now reside in the sync/ subdir of the dbpath */
384 			sprintf(db->_path, "%ssync/%s%s", dbpath, db->treename, dbext);
385 		}
386 		_alpm_log(db->handle, ALPM_LOG_DEBUG, "database path for tree %s set to %s\n",
387 				db->treename, db->_path);
388 	}
389 	return db->_path;
390 }
391 
_alpm_db_cmp(const void * d1,const void * d2)392 int _alpm_db_cmp(const void *d1, const void *d2)
393 {
394 	const alpm_db_t *db1 = d1;
395 	const alpm_db_t *db2 = d2;
396 	return strcmp(db1->treename, db2->treename);
397 }
398 
_alpm_db_search(alpm_db_t * db,const alpm_list_t * needles)399 alpm_list_t *_alpm_db_search(alpm_db_t *db, const alpm_list_t *needles)
400 {
401 	const alpm_list_t *i, *j, *k;
402 	alpm_list_t *ret = NULL;
403 
404 	if(!(db->usage & ALPM_DB_USAGE_SEARCH)) {
405 		return NULL;
406 	}
407 
408 	/* copy the pkgcache- we will free the list var after each needle */
409 	alpm_list_t *list = alpm_list_copy(_alpm_db_get_pkgcache(db));
410 
411 	for(i = needles; i; i = i->next) {
412 		char *targ;
413 		regex_t reg;
414 
415 		if(i->data == NULL) {
416 			continue;
417 		}
418 		ret = NULL;
419 		targ = i->data;
420 		_alpm_log(db->handle, ALPM_LOG_DEBUG, "searching for target '%s'\n", targ);
421 
422 		if(regcomp(&reg, targ, REG_EXTENDED | REG_NOSUB | REG_ICASE | REG_NEWLINE) != 0) {
423 			RET_ERR(db->handle, ALPM_ERR_INVALID_REGEX, NULL);
424 		}
425 
426 		for(j = list; j; j = j->next) {
427 			alpm_pkg_t *pkg = j->data;
428 			const char *matched = NULL;
429 			const char *name = pkg->name;
430 			const char *desc = alpm_pkg_get_desc(pkg);
431 
432 			/* check name as regex AND as plain text */
433 			if(name && (regexec(&reg, name, 0, 0, 0) == 0 || strstr(name, targ))) {
434 				matched = name;
435 			}
436 			/* check desc */
437 			else if(desc && regexec(&reg, desc, 0, 0, 0) == 0) {
438 				matched = desc;
439 			}
440 			/* TODO: should we be doing this, and should we print something
441 			 * differently when we do match it since it isn't currently printed? */
442 			if(!matched) {
443 				/* check provides */
444 				for(k = alpm_pkg_get_provides(pkg); k; k = k->next) {
445 					alpm_depend_t *provide = k->data;
446 					if(regexec(&reg, provide->name, 0, 0, 0) == 0) {
447 						matched = provide->name;
448 						break;
449 					}
450 				}
451 			}
452 			if(!matched) {
453 				/* check groups */
454 				for(k = alpm_pkg_get_groups(pkg); k; k = k->next) {
455 					if(regexec(&reg, k->data, 0, 0, 0) == 0) {
456 						matched = k->data;
457 						break;
458 					}
459 				}
460 			}
461 
462 			if(matched != NULL) {
463 				_alpm_log(db->handle, ALPM_LOG_DEBUG,
464 						"search target '%s' matched '%s' on package '%s'\n",
465 						targ, matched, name);
466 				ret = alpm_list_add(ret, pkg);
467 			}
468 		}
469 
470 		/* Free the existing search list, and use the returned list for the
471 		 * next needle. This allows for AND-based package searching. */
472 		alpm_list_free(list);
473 		list = ret;
474 		regfree(&reg);
475 	}
476 
477 	return ret;
478 }
479 
480 /* Returns a new package cache from db.
481  * It frees the cache if it already exists.
482  */
load_pkgcache(alpm_db_t * db)483 static int load_pkgcache(alpm_db_t *db)
484 {
485 	_alpm_db_free_pkgcache(db);
486 
487 	_alpm_log(db->handle, ALPM_LOG_DEBUG, "loading package cache for repository '%s'\n",
488 			db->treename);
489 	if(db->ops->populate(db) == -1) {
490 		_alpm_log(db->handle, ALPM_LOG_DEBUG,
491 				"failed to load package cache for repository '%s'\n", db->treename);
492 		return -1;
493 	}
494 
495 	db->status |= DB_STATUS_PKGCACHE;
496 	return 0;
497 }
498 
free_groupcache(alpm_db_t * db)499 static void free_groupcache(alpm_db_t *db)
500 {
501 	alpm_list_t *lg;
502 
503 	if(db == NULL || !(db->status & DB_STATUS_GRPCACHE)) {
504 		return;
505 	}
506 
507 	_alpm_log(db->handle, ALPM_LOG_DEBUG,
508 			"freeing group cache for repository '%s'\n", db->treename);
509 
510 	for(lg = db->grpcache; lg; lg = lg->next) {
511 		_alpm_group_free(lg->data);
512 		lg->data = NULL;
513 	}
514 	FREELIST(db->grpcache);
515 	db->status &= ~DB_STATUS_GRPCACHE;
516 }
517 
_alpm_db_free_pkgcache(alpm_db_t * db)518 void _alpm_db_free_pkgcache(alpm_db_t *db)
519 {
520 	if(db == NULL || !(db->status & DB_STATUS_PKGCACHE)) {
521 		return;
522 	}
523 
524 	_alpm_log(db->handle, ALPM_LOG_DEBUG,
525 			"freeing package cache for repository '%s'\n", db->treename);
526 
527 	if(db->pkgcache) {
528 		alpm_list_free_inner(db->pkgcache->list,
529 				(alpm_list_fn_free)_alpm_pkg_free);
530 		_alpm_pkghash_free(db->pkgcache);
531 	}
532 	db->status &= ~DB_STATUS_PKGCACHE;
533 
534 	free_groupcache(db);
535 }
536 
_alpm_db_get_pkgcache_hash(alpm_db_t * db)537 alpm_pkghash_t *_alpm_db_get_pkgcache_hash(alpm_db_t *db)
538 {
539 	if(db == NULL) {
540 		return NULL;
541 	}
542 
543 	if(!(db->status & DB_STATUS_VALID)) {
544 		RET_ERR(db->handle, ALPM_ERR_DB_INVALID, NULL);
545 	}
546 
547 	if(!(db->status & DB_STATUS_PKGCACHE)) {
548 		if(load_pkgcache(db)) {
549 			/* handle->error set in local/sync-db-populate */
550 			return NULL;
551 		}
552 	}
553 
554 	return db->pkgcache;
555 }
556 
_alpm_db_get_pkgcache(alpm_db_t * db)557 alpm_list_t *_alpm_db_get_pkgcache(alpm_db_t *db)
558 {
559 	alpm_pkghash_t *hash = _alpm_db_get_pkgcache_hash(db);
560 
561 	if(hash == NULL) {
562 		return NULL;
563 	}
564 
565 	return hash->list;
566 }
567 
568 /* "duplicate" pkg then add it to pkgcache */
_alpm_db_add_pkgincache(alpm_db_t * db,alpm_pkg_t * pkg)569 int _alpm_db_add_pkgincache(alpm_db_t *db, alpm_pkg_t *pkg)
570 {
571 	alpm_pkg_t *newpkg = NULL;
572 
573 	if(db == NULL || pkg == NULL || !(db->status & DB_STATUS_PKGCACHE)) {
574 		return -1;
575 	}
576 
577 	if(_alpm_pkg_dup(pkg, &newpkg)) {
578 		/* we return memory on "non-fatal" error in _alpm_pkg_dup */
579 		_alpm_pkg_free(newpkg);
580 		return -1;
581 	}
582 
583 	_alpm_log(db->handle, ALPM_LOG_DEBUG, "adding entry '%s' in '%s' cache\n",
584 						newpkg->name, db->treename);
585 	if(newpkg->origin == ALPM_PKG_FROM_FILE) {
586 		free(newpkg->origin_data.file);
587 	}
588 	newpkg->origin = (db->status & DB_STATUS_LOCAL)
589 		? ALPM_PKG_FROM_LOCALDB
590 		: ALPM_PKG_FROM_SYNCDB;
591 	newpkg->origin_data.db = db;
592 	if(_alpm_pkghash_add_sorted(&db->pkgcache, newpkg) == NULL) {
593 		_alpm_pkg_free(newpkg);
594 		RET_ERR(db->handle, ALPM_ERR_MEMORY, -1);
595 	}
596 
597 	free_groupcache(db);
598 
599 	return 0;
600 }
601 
_alpm_db_remove_pkgfromcache(alpm_db_t * db,alpm_pkg_t * pkg)602 int _alpm_db_remove_pkgfromcache(alpm_db_t *db, alpm_pkg_t *pkg)
603 {
604 	alpm_pkg_t *data = NULL;
605 
606 	if(db == NULL || pkg == NULL || !(db->status & DB_STATUS_PKGCACHE)) {
607 		return -1;
608 	}
609 
610 	_alpm_log(db->handle, ALPM_LOG_DEBUG, "removing entry '%s' from '%s' cache\n",
611 						pkg->name, db->treename);
612 
613 	db->pkgcache = _alpm_pkghash_remove(db->pkgcache, pkg, &data);
614 	if(data == NULL) {
615 		/* package not found */
616 		_alpm_log(db->handle, ALPM_LOG_DEBUG, "cannot remove entry '%s' from '%s' cache: not found\n",
617 							pkg->name, db->treename);
618 		return -1;
619 	}
620 
621 	_alpm_pkg_free(data);
622 
623 	free_groupcache(db);
624 
625 	return 0;
626 }
627 
_alpm_db_get_pkgfromcache(alpm_db_t * db,const char * target)628 alpm_pkg_t *_alpm_db_get_pkgfromcache(alpm_db_t *db, const char *target)
629 {
630 	if(db == NULL) {
631 		return NULL;
632 	}
633 
634 	alpm_pkghash_t *pkgcache = _alpm_db_get_pkgcache_hash(db);
635 	if(!pkgcache) {
636 		return NULL;
637 	}
638 
639 	return _alpm_pkghash_find(pkgcache, target);
640 }
641 
642 /* Returns a new group cache from db.
643  */
load_grpcache(alpm_db_t * db)644 static int load_grpcache(alpm_db_t *db)
645 {
646 	alpm_list_t *lp;
647 
648 	if(db == NULL) {
649 		return -1;
650 	}
651 
652 	_alpm_log(db->handle, ALPM_LOG_DEBUG, "loading group cache for repository '%s'\n",
653 			db->treename);
654 
655 	for(lp = _alpm_db_get_pkgcache(db); lp; lp = lp->next) {
656 		const alpm_list_t *i;
657 		alpm_pkg_t *pkg = lp->data;
658 
659 		for(i = alpm_pkg_get_groups(pkg); i; i = i->next) {
660 			const char *grpname = i->data;
661 			alpm_list_t *j;
662 			alpm_group_t *grp = NULL;
663 			int found = 0;
664 
665 			/* first look through the group cache for a group with this name */
666 			for(j = db->grpcache; j; j = j->next) {
667 				grp = j->data;
668 
669 				if(strcmp(grp->name, grpname) == 0
670 						&& !alpm_list_find_ptr(grp->packages, pkg)) {
671 					grp->packages = alpm_list_add(grp->packages, pkg);
672 					found = 1;
673 					break;
674 				}
675 			}
676 			if(found) {
677 				continue;
678 			}
679 			/* we didn't find the group, so create a new one with this name */
680 			grp = _alpm_group_new(grpname);
681 			if(!grp) {
682 				free_groupcache(db);
683 				return -1;
684 			}
685 			grp->packages = alpm_list_add(grp->packages, pkg);
686 			db->grpcache = alpm_list_add(db->grpcache, grp);
687 		}
688 	}
689 
690 	db->status |= DB_STATUS_GRPCACHE;
691 	return 0;
692 }
693 
_alpm_db_get_groupcache(alpm_db_t * db)694 alpm_list_t *_alpm_db_get_groupcache(alpm_db_t *db)
695 {
696 	if(db == NULL) {
697 		return NULL;
698 	}
699 
700 	if(!(db->status & DB_STATUS_VALID)) {
701 		RET_ERR(db->handle, ALPM_ERR_DB_INVALID, NULL);
702 	}
703 
704 	if(!(db->status & DB_STATUS_GRPCACHE)) {
705 		load_grpcache(db);
706 	}
707 
708 	return db->grpcache;
709 }
710 
_alpm_db_get_groupfromcache(alpm_db_t * db,const char * target)711 alpm_group_t *_alpm_db_get_groupfromcache(alpm_db_t *db, const char *target)
712 {
713 	alpm_list_t *i;
714 
715 	if(db == NULL || target == NULL || strlen(target) == 0) {
716 		return NULL;
717 	}
718 
719 	for(i = _alpm_db_get_groupcache(db); i; i = i->next) {
720 		alpm_group_t *info = i->data;
721 
722 		if(strcmp(info->name, target) == 0) {
723 			return info;
724 		}
725 	}
726 
727 	return NULL;
728 }
729