ADDED smallxrm.c Index: smallxrm.c ================================================================== --- /dev/null +++ smallxrm.c @@ -0,0 +1,390 @@ +#if 0 +gcc -s -O2 -c smallxrm.c +exit +#endif + +/* + Small XRM (X Resource Manager) in C. + Public domain. +*/ + +#include +#include +#include +#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->kk?-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;il.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;il.n;i++) xrm_destroy(db->l.p[i].x); + my_realloc(db->l.p,0); + for(i=0;it.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;il.n;i++) if(r=cb(db,usr,1,db->l.p[i].k)) return r; + for(i=0;it.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;it.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;il.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; +} + ADDED smallxrm.h Index: smallxrm.h ================================================================== --- /dev/null +++ smallxrm.h @@ -0,0 +1,140 @@ +/* + Small XRM (X Resource Manager) in C. + Public domain. +*/ + +#ifndef SMALLXRM_H_INCLUDED +#define SMALLXRM_H_INCLUDED + +typedef struct xrm_db xrm_db; // type of database nodes +typedef unsigned int xrm_quark; // type of quarks + +#define xrm_nullq ((xrm_quark)0) // not a valid quark +#define xrm_anyq ((xrm_quark)1) // the "?" quark + +// Memory handling: Any xrm_db* values returned by these functions are +// invalid if xrm_destroy() is subsequently used on that node or on any of +// its ancestor nodes. Any string pointers returned by these functions are +// invalid if the node it belongs to is destroyed, if the resource value is +// changed, or if xrm_annihilate() is called. Do not free them by yourself. + +void xrm_annihilate(void); +// Releases all memory used by this library. Does nothing if not +// initialized. You must first call xrm_destroy() on any existing resource +// databases you may have allocated. After xrm_annihilate() is called, +// nothing else should be called without calling xrm_init() first. You may +// safely terminate the program without calling this function, since the +// only thing it does is free memory, which is automatically done when the +// program terminates anyways. + +xrm_db*xrm_create(void); +// Creates a new empty resource database. + +void xrm_destroy(xrm_db*db); +// Destroy an existing resource database. All sub-databases are also +// destroyed. The memory is freed. + +void*xrm_enumerate(xrm_db*db,void*(*cb)(xrm_db*,void*,int,xrm_quark),void*usr); +// Calls the given callback function for each child node of the resource +// database; the first two arguments are the same as db and usr given to +// this function, while the next is 1 if loose or 0 if tight, and the next +// is the quark that is in use. You can then call xrm_sub() in order to +// access the sub-database it contains. If the callback returns null, then +// the enumeration continues, otherwise it returns with the same value. It +// only goes one level deep; you can use it recursively to go more deep. + +const char*xrm_get(xrm_db*db); +// Retrieves the value of the resource at the root of the given database. +// If there is no value, the result is a null pointer. + +const char*xrm_get_resource(xrm_db*db,const xrm_quark*ns,const xrm_quark*cs,int len); +// Retrieves the value of a given resource, given the root node, list of +// quarks for the name of the query, list of quarks for the class of the +// query, and the length of the name and class list. Both list pointers +// must be non-null, but you may give the same pointers for each. If there +// is no such resource, the result is a null pointer. + +int xrm_init(void*(*f)(void*,size_t)); +// Initialize the library; must be called exactly once, before anything +// else is called. The argument should be realloc, or your own function +// that does the same thing; all dynamic memory allocation done by the +// library will use the function you give for this purpose. Return value +// is 0 if success or -1 if error. + +int xrm_init_quarks(const char*const*list); +// Optional. If you call it, it must be called after xrm_init() is called +// before any other library functions are called. The argument is a null +// terminated list of strings, all of which must be unique, and none of +// them may be "?". They are assigned constant quark numbers, where the +// first string is the name of quark number 2, the next being quark number +// 3, and so on. You can use this in order to make compile-time constants +// for the quarks used in your program. The library does not make copies +// of the strings; they must exist for the entire duration of the library. +// The return value is 0 if OK or -1 if not OK. + +int xrm_link(xrm_db*db,int loose,xrm_quark q,xrm_db*ins); +// Insert a node (the fourth argument) as child of the node given by the +// first argument. The node is now "owned" by the parent; if you then call +// xrm_destroy() without unlinking it, it will destroy that one too. You +// normally do not need to use this function. Return value is 0 if it is +// successful or -1 in case of error. + +int xrm_load(xrm_db*db,FILE*fp,int o); +// Load a resource database from an open file handle. #include is not +// currently implemented. It loads into the given database, and returns 0 +// if successful or -1 if error. The third argument is nonzero if it +// should override existing resources, or zero if it doesn't. + +int xrm_load_line(xrm_db*db,const char*s,int o); +// Load a resource from a string, which must be a single line; in case of +// any line breaks, they and anything after them are ignored. Line +// continuation is not implemented by xrm_load_line() (but xrm_load() does +// implement it). + +xrm_quark xrm_make_quark(const char*name,int addnew); +// Make a new quark (or retrieves an existing quark by name) and returns +// it. The return value is zero (xrm_nullq) if it does not exist. The +// second argument is zero to retrieve only existing quarks, or nonzero if +// it should make a new quark if there isn't an existing quark with the +// given name. If the first argument is null and the second argument is +// nonzero, then it makes a new unique quark. The library makes a copy of +// the passed string if a new quark is made; you need not copy it yourself. + +int xrm_merge(xrm_db*to,xrm_db*from,int o); +// Merge the second database into the first one. Returns 0 if success or +// -1 if error. The third argument is zero if the existing resources have +// priority or nonzero if the new ones do. + +const char*xrm_name(xrm_quark n); +// Returns the name of the given quark. If there is no such quark, or if +// it is xrm_nullq, or if it has no name, the result is null. + +int xrm_put(xrm_db*db,const char*v,int o); +// Set the value of the node. First argument is the node, second argument +// is the value (the library makes a copy of it) or null to delete the +// value, and third argument is nonzero to override an existing value or +// zero to not change an existing value. Returns 0 if successful. + +int xrm_put_resource(xrm_db*db,const xrm_quark*q,const char*b,const char*v,int o); +// Set the value of a resource. The second argument is the quark list, the +// third argument is the binding list, the fourth argument is the value, +// and the fifth argument specifies to override or not. The binding list +// is a string containing characters '*' and '.' and must have the same +// length as the quark list. + +void*xrm_search(xrm_db*db,const xrm_quark*ns,const xrm_quark*cs,int len,void*(*cb)(xrm_db*,void*),void*usr); +// Perform a search of the given resource database, given the list of +// quarks for name of the query, quarks for class of the query, length of +// the quark lists, a callback function, and the user value for the +// callback function. Both the name and class list must be not null, but +// they may be the same pointer. It calls the callback function for each +// node in priority order, and stops once the callback function returns +// not null, and then xrm_search() returns the same value. + +xrm_db*xrm_sub(xrm_db*db,int loose,xrm_quark q); +// Access a child node of the given node. The second argument is zero for +// a tight binding or nonzero for loose. The third is the quark. If no +// such node exists, a new node with no value or children is created. + +#endif +