#if 0
gcc -s -O2 -c smallxrm.c
exit
#endif
/*
Small XRM (X Resource Manager) in C.
Public domain.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "smallxrm.h"
typedef struct xrm_pair {
xrm_quark k;
xrm_db*x;
} xrm_pair;
typedef struct xrm_map {
int n;
xrm_pair*p;
} xrm_map;
typedef struct xrm_db {
xrm_map l;
xrm_map t;
char*v;
} xrm_db;
#define BUFINC 1024
static char anyq_name[2]="?";
static void*(*my_realloc)(void*,size_t);
static int nquarks;
static xrm_quark*quarklook;
static char**quarknames;
static xrm_quark keyquark=0;
static const char*keyname;
static char*inbuf;
static int inbufsize;
static int staticquarks;
static void*cb_get_resource(xrm_db*db,void*usr) {
return db->v;
}
static int db_compar(const void*a,const void*b) {
const xrm_pair*x=a;
const xrm_pair*y=b;
return x->k<y->k?-1:x->k==y->k?0:1;
}
static char*my_strdup(const char*p) {
char*s=my_realloc(0,strlen(p)+1);
if(!s) return 0;
strcpy(s,p);
return s;
}
static int quarklook_compar(const void*a,const void*b) {
xrm_quark x=*(const xrm_quark*)a;
xrm_quark y=*(const xrm_quark*)b;
return strcmp(x?quarknames[x-1]?:"":keyname,y?quarknames[y-1]?:"":keyname);
}
void xrm_annihilate(void) {
int i;
if(!my_realloc) return;
for(i=staticquarks;i<nquarks;i++) my_realloc(quarknames[i],0);
my_realloc(quarknames,0);
nquarks=0;
quarklook=0;
quarknames=0;
my_realloc(inbuf,0);
inbuf=0;
inbufsize=0;
my_realloc=0;
}
xrm_db*xrm_create(void) {
xrm_db*db=my_realloc(0,sizeof(xrm_db));
if(!db) return 0;
db->l.n=0;
db->l.p=0;
db->t.n=0;
db->t.p=0;
db->v=0;
return db;
}
void xrm_destroy(xrm_db*db) {
int i;
if(!db) return;
my_realloc(db->v,0);
for(i=0;i<db->l.n;i++) xrm_destroy(db->l.p[i].x);
my_realloc(db->l.p,0);
for(i=0;i<db->t.n;i++) xrm_destroy(db->t.p[i].x);
my_realloc(db->t.p,0);
my_realloc(db,0);
}
void*xrm_enumerate(xrm_db*db,void*(*cb)(xrm_db*,void*,int,xrm_quark),void*usr) {
int i;
void*r;
if(!db) return 0;
for(i=0;i<db->l.n;i++) if(r=cb(db,usr,1,db->l.p[i].k)) return r;
for(i=0;i<db->t.n;i++) if(r=cb(db,usr,0,db->t.p[i].k)) return r;
return 0;
}
const char*xrm_get(xrm_db*db) {
return db?db->v:0;
}
const char*xrm_get_resource(xrm_db*db,const xrm_quark*ns,const xrm_quark*cs,int len) {
if(!db) return 0;
return xrm_search(db,ns,cs,len,cb_get_resource,0);
}
int xrm_init(void*(*f)(void*,size_t)) {
if(my_realloc || !f) return -1;
my_realloc=f;
nquarks=1;
quarknames=my_realloc(0,sizeof(char*));
if(!quarknames) goto bad;
quarklook=my_realloc(0,sizeof(xrm_quark));
if(!quarklook) {
my_realloc(quarknames,0);
quarknames=0;
goto bad;
}
*quarklook=xrm_anyq;
*quarknames=anyq_name;
inbuf=0;
inbufsize=0;
staticquarks=1;
return 0;
bad:
my_realloc=0;
return -1;
}
int xrm_init_quarks(const char*const*list) {
void*mem;
int i=0;
if(!my_realloc || !list || nquarks!=1 || staticquarks!=1) return -1;
while(list[i]) i++;
mem=my_realloc(quarknames,(i+1)*sizeof(char*));
if(!mem) return -1;
quarknames=mem;
mem=my_realloc(quarklook,(i+1)*sizeof(xrm_quark));
if(!mem) return -1;
quarklook=mem;
nquarks=staticquarks=i+1;
for(i=0;list[i];i++) {
quarknames[i+1]=(char*)(list[i]);
quarklook[i+1]=i+2;
}
qsort(quarklook,nquarks,sizeof(xrm_quark),quarklook_compar);
return 0;
}
int xrm_link(xrm_db*db,int loose,xrm_quark q,xrm_db*ins) {
xrm_map*m;
xrm_pair*p;
xrm_pair k={q,ins};
if(!db || !q) return 0;
m=loose?&db->l:&db->t;
if(m->n) {
p=bsearch(&k,m->p,m->n,sizeof(xrm_pair),db_compar);
if(p) {
*p=k;
return 0;
}
}
p=my_realloc(m->p,(m->n+1)*sizeof(xrm_pair));
if(!p) return -1;
p[m->n]=k;
qsort(p,++m->n,sizeof(xrm_pair),db_compar);
m->p=p;
return 0;
}
int xrm_load(xrm_db*db,FILE*fp,int o) {
int bs,c,n,x;
if(!inbuf) {
inbuf=my_realloc(0,BUFINC+2);
if(!inbuf) return -1;
inbufsize=BUFINC;
}
if(!db || !fp) return -1;
for(c=0;c!=EOF;) {
for(x=bs=0;(c=fgetc(fp))!=EOF;) {
if(c=='\r') continue;
if(c=='\\') {
bs^=1;
} else if(c=='\n') {
if(bs--) x--; else break;
} else {
bs=0;
}
inbuf[x++]=c;
if(x>=inbufsize) {
char*buf=my_realloc(inbuf,inbufsize+BUFINC+2);
if(!buf) return -1;
inbuf=buf;
inbufsize+=BUFINC;
}
}
inbuf[x]=0;
xrm_load_line(db,inbuf,o);
}
return 0;
}
int xrm_load_line(xrm_db*db,const char*s,int o) {
int c;
int loose=0;
char*p;
char*q=0;
char*r;
if(!s) return -1;
if(!*s || *s=='!') return 0;
if(*s=='#') return -1;
if(s!=inbuf) {
int i=strlen(s)+1;
if(i>=inbufsize) {
char*buf=my_realloc(inbuf,i+2);
if(!buf) return -1;
inbuf=buf;
inbufsize=i;
}
strcpy(inbuf,s);
}
p=inbuf;
while(*p==' ' || *p=='\t') s++;
if(!*p) return 0;
while(c=*p++) {
if(c=='.' || c=='*' || c==':') {
if(q) {
r=p-1;
*r--=0;
while(r>q && (*r==' ' || *r=='\t')) *r--=0;
db=xrm_sub(db,loose,xrm_make_quark(q,1));
if(!db) return -1;
loose=0;
q=0;
}
if(c=='*') loose=1;
if(c==':') break;
} else if(c!=' ' && c!='\t' && !q) {
q=p-1;
}
}
if(!c) return -1;
if(db->v && !o) return 0;
while(*p==' ' || *p=='\t') p++;
q=p;
while(c=*p++) {
if(c=='\\') {
if(p[0]>='0' && p[0]<'8' && p[1]>='0' && p[1]<'8' && p[2]>='0' && p[2]<'8') {
r=p+2;
*r=((p[0]-'0')<<6)|((p[1]-'0')<<3)|(p[2]-'0');
while(*r) r[-3]=*r,++r;
r[-3]=0;
} else {
if(*p=='n') *p='\n';
r=p;
while(*r) r[-1]=*r,++r;
r[-1]=0;
}
} else if(c=='\r' || c=='\n') {
break;
}
}
*p=0;
return xrm_put(db,q,1);
}
xrm_quark xrm_make_quark(const char*name,int addnew) {
xrm_quark*q;
if(name && !*name) return 0;
keyname=name;
if(!name) {
if(!addnew) return 0;
q=0;
} else {
q=bsearch(&keyquark,quarklook,nquarks,sizeof(xrm_quark),quarklook_compar);
}
if(addnew && !q) {
xrm_quark*ql=my_realloc(quarklook,(nquarks+1)*sizeof(xrm_quark));
char**qn=my_realloc(quarknames,(nquarks+1)*sizeof(char*));
if(ql) quarklook=ql;
if(qn) quarknames=qn;
if(!ql || !qn) return 0;
if(name) {
qn[nquarks]=my_strdup(name);
if(!qn[nquarks]) return 0;
} else {
qn[nquarks]=0;
}
ql[nquarks]=++nquarks;
qsort(quarklook,nquarks,sizeof(xrm_quark),quarklook_compar);
return nquarks;
} else {
return q?*q:0;
}
}
int xrm_merge(xrm_db*to,xrm_db*from,int o) {
int i;
if(!from) return 0;
if(!to) return -1;
if(xrm_put(to,from->v,o)) return -1;
for(i=0;i<from->t.n;i++) if(xrm_merge(xrm_sub(to,0,from->t.p[i].k),from->t.p[i].x,o)) return -1;
for(i=0;i<from->l.n;i++) if(xrm_merge(xrm_sub(to,1,from->l.p[i].k),from->l.p[i].x,o)) return -1;
return 0;
}
const char*xrm_name(xrm_quark n) {
if(!n || n>nquarks) return 0;
return quarknames[n-1];
}
int xrm_put(xrm_db*db,const char*v,int o) {
char*s;
if(!db) return -1;
if(db->v && !o) return 0;
if(v) {
s=my_strdup(v);
if(!s) return -1;
my_realloc(db->v,0);
db->v=s;
} else {
my_realloc(db->v,0);
db->v=0;
}
return 0;
}
int xrm_put_resource(xrm_db*db,const xrm_quark*q,const char*b,const char*v,int o) {
while(*b) {
if(*b!='.' && *b!='*') return -1;
db=xrm_sub(db,*b=='*',*q);
q++; b++;
}
return xrm_put(db,v,o);
}
void*xrm_search(xrm_db*db,const xrm_quark*ns,const xrm_quark*cs,int len,void*(*cb)(xrm_db*,void*),void*usr) {
xrm_db*p;
void*r;
if(!len) return cb(db,usr);
if((p=xrm_sub(db,0,*ns)) && (r=xrm_search(p,ns+1,cs+1,len-1,cb,usr))) return r;
if(ns!=cs && (p=xrm_sub(db,0,*cs)) && (r=xrm_search(p,ns+1,cs+1,len-1,cb,usr))) return r;
if((p=xrm_sub(db,0,xrm_anyq)) && (r=xrm_search(p,ns+1,cs+1,len-1,cb,usr))) return r;
again:
if((p=xrm_sub(db,1,*ns)) && (r=xrm_search(p,ns+1,cs+1,len-1,cb,usr))) return r;
if(ns!=cs && (p=xrm_sub(db,1,*cs)) && (r=xrm_search(p,ns+1,cs+1,len-1,cb,usr))) return r;
if((p=xrm_sub(db,1,xrm_anyq)) && (r=xrm_search(p,ns+1,cs+1,len-1,cb,usr))) return r;
ns++; cs++;
if(--len) goto again;
return 0;
}
xrm_db*xrm_sub(xrm_db*db,int loose,xrm_quark q) {
xrm_map*m;
xrm_pair*p;
xrm_pair k={q,0};
if(!db || !q) return 0;
m=loose?&db->l:&db->t;
if(m->n) {
p=bsearch(&k,m->p,m->n,sizeof(xrm_pair),db_compar);
if(p) return p->x;
}
k.x=xrm_create();
if(!k.x) return 0;
p=my_realloc(m->p,(m->n+1)*sizeof(xrm_pair));
if(!p) {
my_realloc(k.x,0);
return 0;
}
p[m->n]=k;
qsort(p,++m->n,sizeof(xrm_pair),db_compar);
m->p=p;
return k.x;
}