| git.druid.rocks | index | druid520 | q | src/ | db.c |
src/db.c
#include "config.h"
#ifdef OS_NETBSD_C
#define _NETBSD_SOURCE 1
#endif
#define _POSIX_C_SOURCE 200809L
#include <string.h>
#include <dirent.h>
#include <sys/stat.h>
#include "q.h"
/* the installed-package database: <db>/<pkg>/ holding three files --
* "name" (the package name), "ver" (its version), and "manifest" (one
* prefix-relative path per line, the files that package owns). */
void
db_pkgdir(const char* db, const char* pkg, char* out, size_t max)
{
if(pathjoin(out, max, db, pkg) == 0)
{
die("path too long");
return;
}
}
long
db_has(const char* db, const char* pkg)
{
char pkgdir[MAXPATH];
struct stat st;
db_pkgdir(db, pkg, pkgdir, sizeof(pkgdir));
if(lstat(pkgdir, &st) == 0 && S_ISDIR(st.st_mode))
{
return 1;
}
return 0;
}
void
db_install(const char* db, const char* pkg, const char* name, const char* ver,
const struct snap* manifest)
{
char pkgdir[MAXPATH];
char fpath[MAXPATH];
char buf[MAXVAL];
size_t off;
db_pkgdir(db, pkg, pkgdir, sizeof(pkgdir));
makedirs(pkgdir);
off = 0;
if(bufcat(buf, sizeof(buf), &off, name) == 0
|| bufcat(buf, sizeof(buf), &off, "\n") == 0)
{
die("pkg name too long");
return;
}
if(pathjoin(fpath, sizeof(fpath), pkgdir, "name") == 0)
{
die("path too long");
return;
}
if(writefile(fpath, buf, (long)off) < 0)
{
die("failed writing db name");
return;
}
off = 0;
if(bufcat(buf, sizeof(buf), &off, ver) == 0
|| bufcat(buf, sizeof(buf), &off, "\n") == 0)
{
die("pkg version too long");
return;
}
if(pathjoin(fpath, sizeof(fpath), pkgdir, "ver") == 0)
{
die("path too long");
return;
}
if(writefile(fpath, buf, (long)off) < 0)
{
die("failed writing db version");
return;
}
if(pathjoin(fpath, sizeof(fpath), pkgdir, "manifest") == 0)
{
die("path too long");
return;
}
snap_writefile(manifest, fpath);
}
void
db_remove(const char* db, const char* pkg)
{
char pkgdir[MAXPATH];
db_pkgdir(db, pkg, pkgdir, sizeof(pkgdir));
rmtree(pkgdir);
}
/* list every installed package's (name, ver) into out (key=name,
* val=ver), in readdir order -- callers sort if they want deterministic
* output. returns the count. a missing db is simply empty. */
long
db_list(const char* db, struct kv* out, long max)
{
DIR* d;
struct dirent* e;
char pkgdir[MAXPATH];
char fpath[MAXPATH];
char buf[MAXVAL];
struct stat st;
long r;
long n = 0;
d = opendir(db);
if(d == NULL)
{
return 0;
}
e = readdir(d);
while(e != NULL && n < max)
{
if(e->d_name[0] != '.')
{
if(pathjoin(pkgdir, sizeof(pkgdir), db, e->d_name) == 0)
{
die("path too long");
return n;
}
if(lstat(pkgdir, &st) == 0 && S_ISDIR(st.st_mode))
{
if(pathjoin(fpath, sizeof(fpath), pkgdir, "name") == 0)
{
die("path too long");
return n;
}
r = readfile(fpath, buf, (long)sizeof(buf) - 1);
if(r > 0)
{
size_t k;
buf[r] = '\0';
k = strlen(buf);
while(k > 0 && (buf[k - 1] == '\n' || buf[k - 1] == '\r'))
{
buf[k - 1] = '\0';
k = k - 1;
}
if(copystr(out[n].key, sizeof(out[n].key), buf) == 0)
{
die("pkg name too long");
return n;
}
}
else if(copystr(out[n].key, sizeof(out[n].key), e->d_name) == 0)
{
die("pkg name too long");
return n;
}
if(pathjoin(fpath, sizeof(fpath), pkgdir, "ver") == 0)
{
die("path too long");
return n;
}
r = readfile(fpath, buf, (long)sizeof(buf) - 1);
if(r > 0)
{
size_t k;
buf[r] = '\0';
k = strlen(buf);
while(k > 0 && (buf[k - 1] == '\n' || buf[k - 1] == '\r'))
{
buf[k - 1] = '\0';
k = k - 1;
}
if(copystr(out[n].val, sizeof(out[n].val), buf) == 0)
{
die("pkg version too long");
return n;
}
}
else if(copystr(out[n].val, sizeof(out[n].val), "?") == 0)
{
die("pkg version too long");
return n;
}
n = n + 1;
}
}
e = readdir(d);
}
closedir(d);
return n;
}