| git.druid.rocks | index | druid520 | q | src/ | conf.c |
src/conf.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 <unistd.h>
#include "q.h"
/* a key="value" line grammar (value must be double-quoted) -- the same
* grammar q's recipes use for their name="..." / ver="..." header lines,
* so one parser serves recipe metadata. */
long
parseconf(const char* buf, long n, struct kv* kv, long max)
{
long i = 0;
long nkv = 0;
long ki;
long vi;
while(i < n && nkv < max)
{
ki = 0;
while(i < n && buf[i] != '=' && buf[i] != '\n' && ki < MAXKEY - 1)
{
kv[nkv].key[ki] = buf[i];
ki = ki + 1;
i = i + 1;
}
kv[nkv].key[ki] = '\0';
while(i < n && buf[i] != '\n' && buf[i] != '"')
{
i = i + 1;
}
if(i < n && buf[i] == '"')
{
i = i + 1;
vi = 0;
while(i < n && buf[i] != '"' && buf[i] != '\n' && vi < MAXVAL - 1)
{
kv[nkv].val[vi] = buf[i];
vi = vi + 1;
i = i + 1;
}
kv[nkv].val[vi] = '\0';
if(kv[nkv].key[0] != '\0')
{
nkv = nkv + 1;
}
}
while(i < n && buf[i] != '\n')
{
i = i + 1;
}
if(i < n)
{
i = i + 1;
}
}
return nkv;
}
const char*
kvget(struct kv* kv, long nkv, const char* key)
{
long i = 0;
while(i < nkv)
{
if(strcmp(kv[i].key, key) == 0)
{
return kv[i].val;
}
i = i + 1;
}
return NULL;
}
/* strip trailing newlines (and a stray \r) off a line read from the
* config dump. */
static void
chomp(char* s)
{
size_t n = strlen(s);
while(n > 0 && (s[n - 1] == '\n' || s[n - 1] == '\r'))
{
s[n - 1] = '\0';
n = n - 1;
}
}
/* resolve the effective prefix/db/shell/pkgs. /etc/q.cfg (or $Q_CONF) is
* a posix shell script -- the "configure the package manager in general"
* file, so it can hold functions, conditionals, or exports, not just a
* fixed key=value grammar. it is sourced once here (via a shell) to read
* the four values it may set, then sourced again into every recipe by
* runrecipe. anything a config doesn't set falls back to the defaults;
* the Q_PREFIX/Q_DB/Q_SHELL/Q_PKGS environment variables override both. */
void
loadcfg(struct qcfg* c)
{
const char* confpath;
const char* v;
static const char qcmd[] =
". \"$Q_CONF_PATH\"; printf '%s\\n' \"${prefix-}\" \"${db-}\" "
"\"${shell-}\" \"${pkgs-}\"";
confpath = getenv("Q_CONF");
if(confpath == NULL || confpath[0] == '\0')
{
confpath = "/etc/q.cfg";
}
c->prefix[0] = '\0';
c->db[0] = '\0';
c->shell[0] = '\0';
c->pkgs[0] = '\0';
if(copystr(c->conf, sizeof(c->conf), confpath) == 0)
{
die("config path too long");
return;
}
/* a missing config is silently optional (plenty of setups run on
* pure defaults); a present one is sourced through the shell so its
* prefix/db/shell/pkgs -- each possibly empty if unset -- come back
* as four lines, and defaulting happens here in c rather than in the
* shell, so the defaults live in exactly one place. */
if(access(confpath, F_OK) == 0)
{
FILE* f;
setenv("Q_CONF_PATH", confpath, 1);
f = popen(qcmd, "r");
if(f != NULL)
{
char buf[MAXPATH];
if(fgets(buf, sizeof(buf), f) != NULL)
{
chomp(buf);
if(copystr(c->prefix, sizeof(c->prefix), buf) == 0)
{
die("prefix too long");
return;
}
}
if(fgets(buf, sizeof(buf), f) != NULL)
{
chomp(buf);
if(copystr(c->db, sizeof(c->db), buf) == 0)
{
die("db path too long");
return;
}
}
if(fgets(buf, sizeof(buf), f) != NULL)
{
chomp(buf);
if(copystr(c->shell, sizeof(c->shell), buf) == 0)
{
die("shell path too long");
return;
}
}
if(fgets(buf, sizeof(buf), f) != NULL)
{
chomp(buf);
if(copystr(c->pkgs, sizeof(c->pkgs), buf) == 0)
{
die("pkgs path too long");
return;
}
}
pclose(f);
}
}
/* the environment wins over the file, so a single invocation can
* point q at a scratch prefix without touching the config. */
v = getenv("Q_PREFIX");
if(v != NULL && v[0] != '\0')
{
if(copystr(c->prefix, sizeof(c->prefix), v) == 0)
{
die("prefix too long");
return;
}
}
v = getenv("Q_DB");
if(v != NULL && v[0] != '\0')
{
if(copystr(c->db, sizeof(c->db), v) == 0)
{
die("db path too long");
return;
}
}
v = getenv("Q_SHELL");
if(v != NULL && v[0] != '\0')
{
if(copystr(c->shell, sizeof(c->shell), v) == 0)
{
die("shell path too long");
return;
}
}
v = getenv("Q_PKGS");
if(v != NULL && v[0] != '\0')
{
if(copystr(c->pkgs, sizeof(c->pkgs), v) == 0)
{
die("pkgs path too long");
return;
}
}
if(c->prefix[0] == '\0')
{
copystr(c->prefix, sizeof(c->prefix), "/usr/local");
}
if(c->db[0] == '\0')
{
pathjoin(c->db, sizeof(c->db), c->prefix, "var/db/q");
}
if(c->shell[0] == '\0')
{
copystr(c->shell, sizeof(c->shell), "/bin/sh");
}
if(c->pkgs[0] == '\0')
{
pathjoin(c->pkgs, sizeof(c->pkgs), c->prefix, "pkgs");
}
}