| git.druid.rocks | index | druid520 | q | src/ | snap.c |
src/snap.c
#include "config.h"
#ifdef OS_NETBSD_C
#define _NETBSD_SOURCE 1
#endif
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "q.h"
/* the buffer snap_sort's comparator sorts against -- qsort's comparator
* takes no context, so the current snap's buffer is parked in file-scope
* static for the duration of the sort. */
static const char* g_sortbuf;
static int
cmp_off(const void* a, const void* b)
{
const long* ia = (const long*)a;
const long* ib = (const long*)b;
return strcmp(g_sortbuf + *ia, g_sortbuf + *ib);
}
void
snap_init(struct snap* s)
{
s->buf = NULL;
s->cap = 0;
s->len = 0;
s->idx = NULL;
s->icap = 0;
s->n = 0;
}
void
snap_add(struct snap* s, const char* rel)
{
size_t n = strlen(rel);
long need;
if(s->n >= MAXPATHS)
{
die("too many paths in snapshot (increase MAXPATHS)");
return;
}
if((size_t)s->len + n + 1 > (size_t)MAXSNAP)
{
die("snapshot too large (increase MAXSNAP)");
return;
}
need = s->len + (long)n + 1;
if(need > s->cap)
{
long ncap = s->cap;
char* nb;
if(ncap < 4096)
{
ncap = 4096;
}
while(ncap < need)
{
ncap = ncap + ncap;
}
nb = (char*)realloc(s->buf, (size_t)ncap);
if(nb == NULL)
{
die("out of memory");
return;
}
s->buf = nb;
s->cap = ncap;
}
if(s->n >= s->icap)
{
long nicap = s->icap;
long* nidx;
if(nicap < 1024)
{
nicap = 1024;
}
while(nicap <= s->n)
{
nicap = nicap + nicap;
}
nidx = (long*)realloc(s->idx, (size_t)nicap * sizeof(s->idx[0]));
if(nidx == NULL)
{
die("out of memory");
return;
}
s->idx = nidx;
s->icap = nicap;
}
memcpy(s->buf + s->len, rel, n);
s->buf[s->len + (long)n] = '\0';
s->idx[s->n] = s->len;
s->len = s->len + (long)n + 1;
s->n = s->n + 1;
}
void
snap_sort(struct snap* s)
{
g_sortbuf = s->buf;
qsort(s->idx, (size_t)s->n, sizeof(s->idx[0]), cmp_off);
}
const char*
snap_path(const struct snap* s, long i)
{
return s->buf + s->idx[i];
}
long
snap_has(const struct snap* s, const char* rel)
{
long lo = 0;
long hi = s->n - 1;
while(lo <= hi)
{
long mid = lo + (hi - lo) / 2;
int c = strcmp(rel, snap_path(s, mid));
if(c == 0)
{
return 1;
}
if(c < 0)
{
hi = mid - 1;
}
else
{
lo = mid + 1;
}
}
return 0;
}
/* load a manifest (one path per line) into a snap; -1 if missing (the
* caller treats that as empty), and a die (via snap_add) if too large. */
long
snap_readfile(struct snap* s, const char* path)
{
FILE* f;
char line[MAXPATH];
long n = 0;
f = fopen(path, "rb");
if(f == NULL)
{
return -1;
}
while(fgets(line, (int)sizeof(line), f) != NULL)
{
size_t len = strlen(line);
if(len > 0 && line[len - 1] == '\n')
{
line[len - 1] = '\0';
}
if(line[0] != '\0')
{
snap_add(s, line);
n = n + 1;
}
}
fclose(f);
return n;
}
void
snap_writefile(const struct snap* s, const char* path)
{
FILE* f;
long i;
f = fopen(path, "wb");
if(f == NULL)
{
die("failed writing manifest");
return;
}
for(i = 0; i < s->n; i = i + 1)
{
fputs(snap_path(s, i), f);
fputc('\n', f);
}
fclose(f);
}
void
snap_free(struct snap* s)
{
free(s->buf);
free(s->idx);
s->buf = NULL;
s->idx = NULL;
s->cap = 0;
s->icap = 0;
s->len = 0;
s->n = 0;
}
/* the manifest a package owns after an install/update: every path now
* present that either (a) this package already owned before (still
* there), or (b) appeared during the operation (in after, not before).
* for a fresh add, old is empty and this reduces to after - before. */
void
manifest_diff(const struct snap* before, const struct snap* after,
const struct snap* old, struct snap* out)
{
long i;
for(i = 0; i < after->n; i = i + 1)
{
const char* p = snap_path(after, i);
if(snap_has(old, p) || !snap_has(before, p))
{
snap_add(out, p);
}
}
}