4 * Copyright (c) 2006 by Christian Hamar <krics@linuxforum.hu>
5 * Copyright (c) 2006 by Miklos Vajna <vmiklos@frugalware.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, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
30 #include <stdint.h> /* uintmax_t */
35 #include <limits.h> /* PATH_MAX */
36 #include <locale.h> /* setlocale */
40 #include "alpm_list.h"
51 /* This function is used to convert the downloaded db file to the proper backend
54 int _alpm_db_install(pmdb_t
*db
, const char *dbfile
)
58 /* TODO we should not simply unpack the archive, but better parse it and
59 * db_write each entry (see sync_load_dbarchive to get archive content) */
60 _alpm_log(PM_LOG_DEBUG
, "unpacking database '%s'\n", dbfile
);
62 if(_alpm_unpack(dbfile
, db
->path
, NULL
)) {
63 RET_ERR(PM_ERR_SYSTEM
, -1);
66 return unlink(dbfile
);
69 int _alpm_db_open(pmdb_t
*db
)
74 RET_ERR(PM_ERR_DB_NULL
, -1);
77 _alpm_log(PM_LOG_DEBUG
, "opening database from path '%s'\n", db
->path
);
78 db
->handle
= opendir(db
->path
);
79 if(db
->handle
== NULL
) {
80 RET_ERR(PM_ERR_DB_OPEN
, -1);
86 void _alpm_db_close(pmdb_t
*db
)
100 void _alpm_db_rewind(pmdb_t
*db
)
104 if(db
== NULL
|| db
->handle
== NULL
) {
108 rewinddir(db
->handle
);
111 static int _alpm_db_splitname(const char *target
, char *name
, char *version
)
113 /* the format of a db entry is as follows:
114 * package-version-rel/
115 * package name can contain hyphens, so parse from the back- go back
116 * two hyphens and we have split the version from the name.
123 tmp
= strdup(target
);
124 p
= tmp
+ strlen(tmp
);
126 /* do the magic parsing- find the beginning of the version string
127 * by doing two iterations of same loop to lop off two hyphens */
128 for(q
= --p
; *q
&& *q
!= '-'; q
--);
129 for(p
= --q
; *p
&& *p
!= '-'; p
--);
130 if(*p
!= '-' || p
== tmp
) {
134 /* copy into fields and return */
136 strncpy(version
, p
+1, PKG_VERSION_LEN
);
138 /* insert a terminator at the end of the name (on hyphen)- then copy it */
141 strncpy(name
, tmp
, PKG_NAME_LEN
);
148 pmpkg_t
*_alpm_db_scan(pmdb_t
*db
, const char *target
)
150 struct dirent
*ent
= NULL
;
153 char name
[PKG_FULLNAME_LEN
];
161 RET_ERR(PM_ERR_DB_NULL
, NULL
);
164 /* We loop here until we read a valid package. When an iteration of this loop
165 * fails, it means alpm_db_read failed to read a valid package, so we'll read
166 * the next so as not to abort whole-db operations early
170 /* search for a specific package (by name only) */
171 rewinddir(db
->handle
);
172 while(!found
&& (ent
= readdir(db
->handle
)) != NULL
) {
173 if(!strcmp(ent
->d_name
, ".") || !strcmp(ent
->d_name
, "..")) {
176 /* stat the entry, make sure it's a directory */
177 snprintf(path
, PATH_MAX
, "%s/%s", db
->path
, ent
->d_name
);
178 if(stat(path
, &sbuf
) || !S_ISDIR(sbuf
.st_mode
)) {
181 strncpy(name
, ent
->d_name
, PKG_FULLNAME_LEN
);
182 /* truncate the string at the second-to-last hyphen, */
183 /* which will give us the package name */
184 if((ptr
= rindex(name
, '-'))) {
187 if((ptr
= rindex(name
, '-'))) {
190 if(!strcmp(name
, target
)) {
197 } else { /* target == NULL, full scan */
200 ent
= readdir(db
->handle
);
204 if(!strcmp(ent
->d_name
, ".") || !strcmp(ent
->d_name
, "..")) {
208 /* stat the entry, make sure it's a directory */
209 snprintf(path
, PATH_MAX
, "%s/%s", db
->path
, ent
->d_name
);
210 if(!stat(path
, &sbuf
) && S_ISDIR(sbuf
.st_mode
)) {
216 pkg
= _alpm_pkg_new(NULL
, NULL
);
218 _alpm_log(PM_LOG_DEBUG
, "db scan could not find package: %s\n", target
);
221 /* split the db entry name */
222 if(_alpm_db_splitname(ent
->d_name
, pkg
->name
, pkg
->version
) != 0) {
223 _alpm_log(PM_LOG_ERROR
, _("invalid name for database entry '%s'\n"),
230 /* explicitly read with only 'BASE' data, accessors will handle the rest */
231 if(_alpm_db_read(db
, pkg
, INFRQ_BASE
) == -1) {
232 /* TODO removed corrupt entry from the FS here */
235 pkg
->origin
= PKG_FROM_CACHE
;
236 pkg
->origin_data
.db
= db
;
243 int _alpm_db_read(pmdb_t
*db
, pmpkg_t
*info
, pmdbinfrq_t inforeq
)
247 char path
[PATH_MAX
+1];
253 RET_ERR(PM_ERR_DB_NULL
, -1);
256 if(info
== NULL
|| info
->name
[0] == 0 || info
->version
[0] == 0) {
257 _alpm_log(PM_LOG_DEBUG
, "invalid package entry provided to _alpm_db_read, skipping\n");
261 if(info
->origin
== PKG_FROM_FILE
) {
262 _alpm_log(PM_LOG_DEBUG
, "request to read database info for a file-based package '%s', skipping...\n", info
->name
);
266 /* bitmask logic here:
267 * infolevel: 00001111
270 * == to inforeq? nope, we need to load more info. */
271 if((info
->infolevel
& inforeq
) == inforeq
) {
272 /* already loaded this info, do nothing */
275 _alpm_log(PM_LOG_FUNCTION
, _("loading package data for %s : level=%d\n"), info
->name
, inforeq
);
277 /* clear out 'line', to be certain - and to make valgrind happy */
278 memset(line
, 0, 513);
280 snprintf(path
, PATH_MAX
, "%s/%s-%s", db
->path
, info
->name
, info
->version
);
281 if(stat(path
, &buf
)) {
282 /* directory doesn't exist or can't be opened */
283 _alpm_log(PM_LOG_DEBUG
, "cannot find '%s-%s' in db '%s'\n",
284 info
->name
, info
->version
, db
->treename
);
289 if(inforeq
& INFRQ_DESC
) {
290 snprintf(path
, PATH_MAX
, "%s/%s-%s/desc", db
->path
, info
->name
, info
->version
);
291 if((fp
= fopen(path
, "r")) == NULL
) {
292 _alpm_log(PM_LOG_ERROR
, _("could not open file %s: %s\n"), path
, strerror(errno
));
296 if(fgets(line
, 256, fp
) == NULL
) {
300 if(!strcmp(line
, "%FILENAME%")) {
301 /* filename is _new_ - it provides the real name of the package, on the
302 * server, to allow for us to not tie the name of the actual file to the
303 * data of the package
305 if(fgets(info
->filename
, sizeof(info
->filename
), fp
) == NULL
) {
308 _alpm_strtrim(info
->filename
);
309 } else if(!strcmp(line
, "%DESC%")) {
310 if(fgets(info
->desc
, sizeof(info
->desc
), fp
) == NULL
) {
313 _alpm_strtrim(info
->desc
);
314 } else if(!strcmp(line
, "%GROUPS%")) {
315 while(fgets(line
, 512, fp
) && strlen(_alpm_strtrim(line
))) {
316 info
->groups
= alpm_list_add(info
->groups
, strdup(line
));
318 } else if(!strcmp(line
, "%URL%")) {
319 if(fgets(info
->url
, sizeof(info
->url
), fp
) == NULL
) {
322 _alpm_strtrim(info
->url
);
323 } else if(!strcmp(line
, "%LICENSE%")) {
324 while(fgets(line
, 512, fp
) && strlen(_alpm_strtrim(line
))) {
325 info
->licenses
= alpm_list_add(info
->licenses
, strdup(line
));
327 } else if(!strcmp(line
, "%ARCH%")) {
328 if(fgets(info
->arch
, sizeof(info
->arch
), fp
) == NULL
) {
331 _alpm_strtrim(info
->arch
);
332 } else if(!strcmp(line
, "%BUILDDATE%")) {
334 if(fgets(tmp
, sizeof(tmp
), fp
) == NULL
) {
339 char first
= tolower(tmp
[0]);
340 if(first
> 'a' && first
< 'z') {
341 struct tm tmp_tm
= {0}; //initialize to null incase of failure
342 setlocale(LC_TIME
, "C");
343 strptime(tmp
, "%a %b %e %H:%M:%S %Y", &tmp_tm
);
344 info
->builddate
= mktime(&tmp_tm
);
345 setlocale(LC_TIME
, "");
347 info
->builddate
= atol(tmp
);
349 } else if(!strcmp(line
, "%INSTALLDATE%")) {
351 if(fgets(tmp
, sizeof(tmp
), fp
) == NULL
) {
356 char first
= tolower(tmp
[0]);
357 if(first
> 'a' && first
< 'z') {
358 struct tm tmp_tm
= {0}; //initialize to null incase of failure
359 setlocale(LC_TIME
, "C");
360 strptime(tmp
, "%a %b %e %H:%M:%S %Y", &tmp_tm
);
361 info
->installdate
= mktime(&tmp_tm
);
362 setlocale(LC_TIME
, "");
364 info
->installdate
= atol(tmp
);
366 } else if(!strcmp(line
, "%PACKAGER%")) {
367 if(fgets(info
->packager
, sizeof(info
->packager
), fp
) == NULL
) {
370 _alpm_strtrim(info
->packager
);
371 } else if(!strcmp(line
, "%REASON%")) {
373 if(fgets(tmp
, sizeof(tmp
), fp
) == NULL
) {
377 info
->reason
= atol(tmp
);
378 } else if(!strcmp(line
, "%SIZE%") || !strcmp(line
, "%CSIZE%")) {
379 /* NOTE: the CSIZE and SIZE fields both share the "size" field
380 * in the pkginfo_t struct. This can be done b/c CSIZE
381 * is currently only used in sync databases, and SIZE is
382 * only used in local databases.
385 if(fgets(tmp
, sizeof(tmp
), fp
) == NULL
) {
389 info
->size
= atol(tmp
);
390 /* also store this value to isize if isize is unset */
391 if(info
->isize
== 0) {
392 info
->isize
= atol(tmp
);
394 } else if(!strcmp(line
, "%ISIZE%")) {
395 /* ISIZE (installed size) tag only appears in sync repositories,
396 * not the local one. */
398 if(fgets(tmp
, sizeof(tmp
), fp
) == NULL
) {
402 info
->isize
= atol(tmp
);
403 } else if(!strcmp(line
, "%MD5SUM%")) {
404 /* MD5SUM tag only appears in sync repositories,
405 * not the local one. */
406 if(fgets(info
->md5sum
, sizeof(info
->md5sum
), fp
) == NULL
) {
409 } else if(!strcmp(line
, "%REPLACES%")) {
410 /* the REPLACES tag is special -- it only appears in sync repositories,
411 * not the local one. */
412 while(fgets(line
, 512, fp
) && strlen(_alpm_strtrim(line
))) {
413 info
->replaces
= alpm_list_add(info
->replaces
, strdup(line
));
415 } else if(!strcmp(line
, "%FORCE%")) {
416 /* FORCE tag only appears in sync repositories,
417 * not the local one. */
426 if(inforeq
& INFRQ_FILES
) {
427 snprintf(path
, PATH_MAX
, "%s/%s-%s/files", db
->path
, info
->name
, info
->version
);
428 if((fp
= fopen(path
, "r")) == NULL
) {
429 _alpm_log(PM_LOG_ERROR
, _("could not open file %s: %s\n"), path
, strerror(errno
));
432 while(fgets(line
, 256, fp
)) {
434 if(!strcmp(line
, "%FILES%")) {
435 while(fgets(line
, 512, fp
) && strlen(_alpm_strtrim(line
))) {
436 info
->files
= alpm_list_add(info
->files
, strdup(line
));
438 } else if(!strcmp(line
, "%BACKUP%")) {
439 while(fgets(line
, 512, fp
) && strlen(_alpm_strtrim(line
))) {
440 info
->backup
= alpm_list_add(info
->backup
, strdup(line
));
449 if(inforeq
& INFRQ_DEPENDS
) {
450 snprintf(path
, PATH_MAX
, "%s/%s-%s/depends", db
->path
, info
->name
, info
->version
);
451 if((fp
= fopen(path
, "r")) == NULL
) {
452 _alpm_log(PM_LOG_ERROR
, _("could not open file %s: %s\n"), path
, strerror(errno
));
456 fgets(line
, 255, fp
);
458 if(!strcmp(line
, "%DEPENDS%")) {
459 while(fgets(line
, 512, fp
) && strlen(_alpm_strtrim(line
))) {
460 pmdepend_t
*dep
= alpm_splitdep(line
);
461 info
->depends
= alpm_list_add(info
->depends
, dep
);
463 } else if(!strcmp(line
, "%OPTDEPENDS%")) {
464 while(fgets(line
, 512, fp
) && strlen(_alpm_strtrim(line
))) {
465 info
->optdepends
= alpm_list_add(info
->optdepends
, strdup(line
));
467 } else if(!strcmp(line
, "%CONFLICTS%")) {
468 while(fgets(line
, 512, fp
) && strlen(_alpm_strtrim(line
))) {
469 info
->conflicts
= alpm_list_add(info
->conflicts
, strdup(line
));
471 } else if(!strcmp(line
, "%PROVIDES%")) {
472 while(fgets(line
, 512, fp
) && strlen(_alpm_strtrim(line
))) {
473 info
->provides
= alpm_list_add(info
->provides
, strdup(line
));
476 /* TODO: we were going to move these things here, but it should wait.
477 * A better change would be to figure out how to restructure the DB. */
478 /* else if(!strcmp(line, "%REPLACES%")) {
479 * the REPLACES tag is special -- it only appears in sync repositories,
480 * not the local one. *
481 while(fgets(line, 512, fp) && strlen(_alpm_strtrim(line))) {
482 info->replaces = alpm_list_add(info->replaces, strdup(line));
484 } else if(!strcmp(line, "%FORCE%")) {
485 * FORCE tag only appears in sync repositories,
486 * not the local one. *
495 if(inforeq
& INFRQ_DELTAS
) {
496 snprintf(path
, PATH_MAX
, "%s/%s-%s/deltas", db
->path
, info
->name
, info
->version
);
497 if((fp
= fopen(path
, "r"))) {
499 fgets(line
, 255, fp
);
501 if(!strcmp(line
, "%DELTAS%")) {
502 while(fgets(line
, 512, fp
) && strlen(_alpm_strtrim(line
))) {
503 info
->deltas
= alpm_list_add(info
->deltas
, _alpm_delta_parse(line
));
513 if(inforeq
& INFRQ_SCRIPTLET
) {
514 snprintf(path
, PATH_MAX
, "%s/%s-%s/install", db
->path
, info
->name
, info
->version
);
515 if(!stat(path
, &buf
)) {
521 info
->infolevel
|= inforeq
;
532 int _alpm_db_write(pmdb_t
*db
, pmpkg_t
*info
, pmdbinfrq_t inforeq
)
537 alpm_list_t
*lp
= NULL
;
543 if(db
== NULL
|| info
== NULL
) {
547 snprintf(path
, PATH_MAX
, "%s/%s-%s", db
->path
, info
->name
, info
->version
);
548 oldmask
= umask(0000);
550 /* make sure we have a sane umask */
553 if(strcmp(db
->treename
, "local") == 0) {
558 if(inforeq
& INFRQ_DESC
) {
559 _alpm_log(PM_LOG_DEBUG
, "writing %s-%s DESC information back to db\n",
560 info
->name
, info
->version
);
561 snprintf(path
, PATH_MAX
, "%s/%s-%s/desc", db
->path
, info
->name
, info
->version
);
562 if((fp
= fopen(path
, "w")) == NULL
) {
563 _alpm_log(PM_LOG_ERROR
, _("could not open file %s: %s\n"), path
, strerror(errno
));
567 fprintf(fp
, "%%NAME%%\n%s\n\n"
568 "%%VERSION%%\n%s\n\n", info
->name
, info
->version
);
570 fprintf(fp
, "%%DESC%%\n"
571 "%s\n\n", info
->desc
);
574 fputs("%GROUPS%\n", fp
);
575 for(lp
= info
->groups
; lp
; lp
= lp
->next
) {
576 fprintf(fp
, "%s\n", (char *)lp
->data
);
582 fprintf(fp
, "%%URL%%\n"
583 "%s\n\n", info
->url
);
586 fputs("%LICENSE%\n", fp
);
587 for(lp
= info
->licenses
; lp
; lp
= lp
->next
) {
588 fprintf(fp
, "%s\n", (char *)lp
->data
);
593 fprintf(fp
, "%%ARCH%%\n"
594 "%s\n\n", info
->arch
);
596 if(info
->builddate
) {
597 fprintf(fp
, "%%BUILDDATE%%\n"
598 "%ju\n\n", (uintmax_t)info
->builddate
);
600 if(info
->installdate
) {
601 fprintf(fp
, "%%INSTALLDATE%%\n"
602 "%ju\n\n", (uintmax_t)info
->installdate
);
604 if(info
->packager
[0]) {
605 fprintf(fp
, "%%PACKAGER%%\n"
606 "%s\n\n", info
->packager
);
609 /* only write installed size, csize is irrelevant once installed */
610 fprintf(fp
, "%%SIZE%%\n"
611 "%lu\n\n", info
->isize
);
614 fprintf(fp
, "%%REASON%%\n"
615 "%u\n\n", info
->reason
);
619 fprintf(fp
, "%%CSIZE%%\n"
620 "%lu\n\n", info
->size
);
623 fprintf(fp
, "%%ISIZE%%\n"
624 "%lu\n\n", info
->isize
);
627 fprintf(fp
, "%%MD5SUM%%\n"
628 "%s\n\n", info
->md5sum
);
636 if(local
&& (inforeq
& INFRQ_FILES
)) {
637 _alpm_log(PM_LOG_DEBUG
, "writing %s-%s FILES information back to db\n",
638 info
->name
, info
->version
);
639 snprintf(path
, PATH_MAX
, "%s/%s-%s/files", db
->path
, info
->name
, info
->version
);
640 if((fp
= fopen(path
, "w")) == NULL
) {
641 _alpm_log(PM_LOG_ERROR
, _("could not open file %s: %s\n"), path
, strerror(errno
));
646 fprintf(fp
, "%%FILES%%\n");
647 for(lp
= info
->files
; lp
; lp
= lp
->next
) {
648 fprintf(fp
, "%s\n", (char *)lp
->data
);
653 fprintf(fp
, "%%BACKUP%%\n");
654 for(lp
= info
->backup
; lp
; lp
= lp
->next
) {
655 fprintf(fp
, "%s\n", (char *)lp
->data
);
664 if(inforeq
& INFRQ_DEPENDS
) {
665 _alpm_log(PM_LOG_DEBUG
, "writing %s-%s DEPENDS information back to db\n",
666 info
->name
, info
->version
);
667 snprintf(path
, PATH_MAX
, "%s/%s-%s/depends", db
->path
, info
->name
, info
->version
);
668 if((fp
= fopen(path
, "w")) == NULL
) {
669 _alpm_log(PM_LOG_ERROR
, _("could not open file %s: %s\n"), path
, strerror(errno
));
674 fputs("%DEPENDS%\n", fp
);
675 for(lp
= info
->depends
; lp
; lp
= lp
->next
) {
676 char *depstring
= alpm_dep_get_string(lp
->data
);
677 fprintf(fp
, "%s\n", depstring
);
682 if(info
->optdepends
) {
683 fputs("%OPTDEPENDS%\n", fp
);
684 for(lp
= info
->optdepends
; lp
; lp
= lp
->next
) {
685 fprintf(fp
, "%s\n", (char *)lp
->data
);
689 if(info
->conflicts
) {
690 fputs("%CONFLICTS%\n", fp
);
691 for(lp
= info
->conflicts
; lp
; lp
= lp
->next
) {
692 fprintf(fp
, "%s\n", (char *)lp
->data
);
697 fputs("%PROVIDES%\n", fp
);
698 for(lp
= info
->provides
; lp
; lp
= lp
->next
) {
699 fprintf(fp
, "%s\n", (char *)lp
->data
);
705 fputs("%REPLACES%\n", fp
);
706 for(lp
= info
->replaces
; lp
; lp
= lp
->next
) {
707 fprintf(fp
, "%s\n", (char *)lp
->data
);
712 fprintf(fp
, "%%FORCE%%\n"
721 /* nothing needed here (script is automatically extracted) */
733 int _alpm_db_remove(pmdb_t
*db
, pmpkg_t
*info
)
739 if(db
== NULL
|| info
== NULL
) {
740 RET_ERR(PM_ERR_DB_NULL
, -1);
743 snprintf(path
, PATH_MAX
, "%s%s-%s", db
->path
, info
->name
, info
->version
);
744 if(_alpm_rmrf(path
) == -1) {
752 * Return the last update time as number of seconds from the epoch.
753 * Returns 0 if the value is unknown or can't be read.
755 time_t _alpm_db_getlastupdate(const pmdb_t
*db
)
767 snprintf(file
, PATH_MAX
, "%s.lastupdate", db
->path
);
769 /* get the last update time, if it's there */
770 if((fp
= fopen(file
, "r")) == NULL
) {
774 if(fgets(line
, sizeof(line
), fp
)) {
783 * writes the dbpath/.lastupdate file with the value in time
785 int _alpm_db_setlastupdate(const pmdb_t
*db
, time_t time
)
793 if(db
== NULL
|| time
== 0) {
797 snprintf(file
, PATH_MAX
, "%s.lastupdate", db
->path
);
799 if((fp
= fopen(file
, "w")) == NULL
) {
802 if(fprintf(fp
, "%ju", (uintmax_t)time
) <= 0) {
810 /* vim: set ts=2 sw=2 noet: */