Radish alpha
H
rad:z3QDZAW2FAfuLvihrhiyDC9fAD8G9
HardenedBSD Package Manager
Radicle
Git
Include cjson and tinycdb start working on cache with backward compatibility
Baptiste Daroussin committed 15 years ago
commit f343eab9fc994d83fe9cde0f694a9cabfe05f650
parent 3f7c588
54 files changed +5307 -4
added Makefile
@@ -0,0 +1,2 @@
+
SUBDIR= external libpkg pkg
+
.include <bsd.subdir.mk>
added external/Makefile
@@ -0,0 +1,2 @@
+
SUBDIR= cjson tinycdb
+
.include <bsd.subdir.mk>
added external/cjson/Makefile
@@ -0,0 +1,12 @@
+
LIB=    cjson
+
INTERNALLIB=
+
SRCS=   cJSON.c
+

+
WARNS?= 6
+
WFORMAT?=   1
+

+
CFLAGS+= -fPIC
+
NO_MAN=true
+

+
.include <bsd.lib.mk>
+

added external/cjson/cJSON.c
@@ -0,0 +1,490 @@
+
/*
+
  Copyright (c) 2009 Dave Gamble
+

+
  Permission is hereby granted, free of charge, to any person obtaining a copy
+
  of this software and associated documentation files (the "Software"), to deal
+
  in the Software without restriction, including without limitation the rights
+
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+
  copies of the Software, and to permit persons to whom the Software is
+
  furnished to do so, subject to the following conditions:
+

+
  The above copyright notice and this permission notice shall be included in
+
  all copies or substantial portions of the Software.
+

+
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+
  THE SOFTWARE.
+
*/
+

+
// cJSON
+
// JSON parser in C.
+

+
#include <string.h>
+
#include <stdio.h>
+
#include <math.h>
+
#include <stdlib.h>
+
#include <float.h>
+
#include <limits.h>
+
#include <ctype.h>
+
#include "cJSON.h"
+

+
static int cJSON_strcasecmp(const char *s1,const char *s2)
+
{
+
	if (!s1) return (s1==s2)?0:1;if (!s2) return 1;
+
	for(; tolower(*s1) == tolower(*s2); ++s1, ++s2)	if(*s1 == 0)	return 0;
+
	return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2);
+
}
+

+
static void *(*cJSON_malloc)(size_t sz) = malloc;
+
static void (*cJSON_free)(void *ptr) = free;
+

+
static char* cJSON_strdup(const char* str)
+
{
+
      size_t len;
+
      char* copy;
+

+
      len = strlen(str) + 1;
+
      if (!(copy = (char*)cJSON_malloc(len))) return 0;
+
      memcpy(copy,str,len);
+
      return copy;
+
}
+

+
void cJSON_InitHooks(cJSON_Hooks* hooks)
+
{
+
    if (!hooks) { /* Reset hooks */
+
        cJSON_malloc = malloc;
+
        cJSON_free = free;
+
        return;
+
    }
+

+
	cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc;
+
	cJSON_free	 = (hooks->free_fn)?hooks->free_fn:free;
+
}
+

+
// Internal constructor.
+
static cJSON *cJSON_New_Item(void)
+
{
+
	cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON));
+
	if (node) memset(node,0,sizeof(cJSON));
+
	return node;
+
}
+

+
// Delete a cJSON structure.
+
void cJSON_Delete(cJSON *c)
+
{
+
	cJSON *next;
+
	while (c)
+
	{
+
		next=c->next;
+
		if (!(c->type&cJSON_IsReference) && c->child) cJSON_Delete(c->child);
+
		if (!(c->type&cJSON_IsReference) && c->valuestring) cJSON_free(c->valuestring);
+
		if (c->string) cJSON_free(c->string);
+
		cJSON_free(c);
+
		c=next;
+
	}
+
}
+

+
// Parse the input text to generate a number, and populate the result into item.
+
static const char *parse_number(cJSON *item,const char *num)
+
{
+
	double n=0,sign=1,scale=0;int subscale=0,signsubscale=1;
+

+
	// Could use sscanf for this?
+
	if (*num=='-') sign=-1,num++;	// Has sign?
+
	if (*num=='0') num++;			// is zero
+
	if (*num>='1' && *num<='9')	do	n=(n*10.0)+(*num++ -'0');	while (*num>='0' && *num<='9');	// Number?
+
	if (*num=='.') {num++;		do	n=(n*10.0)+(*num++ -'0'),scale--; while (*num>='0' && *num<='9');}	// Fractional part?
+
	if (*num=='e' || *num=='E')		// Exponent?
+
	{	num++;if (*num=='+') num++;	else if (*num=='-') signsubscale=-1,num++;		// With sign?
+
		while (*num>='0' && *num<='9') subscale=(subscale*10)+(*num++ - '0');	// Number?
+
	}
+

+
	n=sign*n*pow(10.0,(scale+subscale*signsubscale));	// number = +/- number.fraction * 10^+/- exponent
+
	
+
	item->valuedouble=n;
+
	item->valueint=(int)n;
+
	item->type=cJSON_Number;
+
	return num;
+
}
+

+
// Render the number nicely from the given item into a string.
+
static char *print_number(cJSON *item)
+
{
+
	char *str;
+
	double d=item->valuedouble;
+
	if (fabs(((double)item->valueint)-d)<=DBL_EPSILON && d<=INT_MAX && d>=INT_MIN)
+
	{
+
		str=(char*)cJSON_malloc(21);	// 2^64+1 can be represented in 21 chars.
+
		sprintf(str,"%d",item->valueint);
+
	}
+
	else
+
	{
+
		str=(char*)cJSON_malloc(64);	// This is a nice tradeoff.
+
		if (fabs(floor(d)-d)<=DBL_EPSILON)			sprintf(str,"%.0f",d);
+
		else if (fabs(d)<1.0e-6 || fabs(d)>1.0e9)	sprintf(str,"%e",d);
+
		else										sprintf(str,"%f",d);
+
	}
+
	return str;
+
}
+

+
// Parse the input text into an unescaped cstring, and populate item.
+
static const char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
+
static const char *parse_string(cJSON *item,const char *str)
+
{
+
	const char *ptr=str+1;char *ptr2;char *out;int len=0;unsigned uc;
+
	if (*str!='\"') return 0;	// not a string!
+
	
+
	while (*ptr!='\"' && (unsigned char)*ptr>31 && ++len) if (*ptr++ == '\\') ptr++;	// Skip escaped quotes.
+
	
+
	out=(char*)cJSON_malloc(len+1);	// This is how long we need for the string, roughly.
+
	if (!out) return 0;
+
	
+
	ptr=str+1;ptr2=out;
+
	while (*ptr!='\"' && (unsigned char)*ptr>31)
+
	{
+
		if (*ptr!='\\') *ptr2++=*ptr++;
+
		else
+
		{
+
			ptr++;
+
			switch (*ptr)
+
			{
+
				case 'b': *ptr2++='\b';	break;
+
				case 'f': *ptr2++='\f';	break;
+
				case 'n': *ptr2++='\n';	break;
+
				case 'r': *ptr2++='\r';	break;
+
				case 't': *ptr2++='\t';	break;
+
				case 'u':	 // transcode utf16 to utf8. DOES NOT SUPPORT SURROGATE PAIRS CORRECTLY.
+
					sscanf(ptr+1,"%4x",&uc);	// get the unicode char.
+
					len=3;if (uc<0x80) len=1;else if (uc<0x800) len=2;ptr2+=len;
+
					
+
					switch (len) {
+
						case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
+
						case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
+
						case 1: *--ptr2 =(uc | firstByteMark[len]);
+
					}
+
					ptr2+=len;ptr+=4;
+
					break;
+
				default:  *ptr2++=*ptr; break;
+
			}
+
			ptr++;
+
		}
+
	}
+
	*ptr2=0;
+
	if (*ptr=='\"') ptr++;
+
	item->valuestring=out;
+
	item->type=cJSON_String;
+
	return ptr;
+
}
+

+
// Render the cstring provided to an escaped version that can be printed.
+
static char *print_string_ptr(const char *str)
+
{
+
	const char *ptr;char *ptr2,*out;int len=0;
+
	
+
	if (!str) return cJSON_strdup("");
+
	ptr=str;while (*ptr && ++len) {if ((unsigned char)*ptr<32 || *ptr=='\"' || *ptr=='\\') len++;ptr++;}
+
	
+
	out=(char*)cJSON_malloc(len+3);
+
	ptr2=out;ptr=str;
+
	*ptr2++='\"';
+
	while (*ptr)
+
	{
+
		if ((unsigned char)*ptr>31 && *ptr!='\"' && *ptr!='\\') *ptr2++=*ptr++;
+
		else
+
		{
+
			*ptr2++='\\';
+
			switch (*ptr++)
+
			{
+
				case '\\':	*ptr2++='\\';	break;
+
				case '\"':	*ptr2++='\"';	break;
+
				case '\b':	*ptr2++='b';	break;
+
				case '\f':	*ptr2++='f';	break;
+
				case '\n':	*ptr2++='n';	break;
+
				case '\r':	*ptr2++='r';	break;
+
				case '\t':	*ptr2++='t';	break;
+
				default: ptr2--;	break;	// eviscerate with prejudice.
+
			}
+
		}
+
	}
+
	*ptr2++='\"';*ptr2++=0;
+
	return out;
+
}
+
// Invote print_string_ptr (which is useful) on an item.
+
static char *print_string(cJSON *item)	{return print_string_ptr(item->valuestring);}
+

+
// Predeclare these prototypes.
+
static const char *parse_value(cJSON *item,const char *value);
+
static char *print_value(cJSON *item,int depth,int fmt);
+
static const char *parse_array(cJSON *item,const char *value);
+
static char *print_array(cJSON *item,int depth,int fmt);
+
static const char *parse_object(cJSON *item,const char *value);
+
static char *print_object(cJSON *item,int depth,int fmt);
+

+
// Utility to jump whitespace and cr/lf
+
static const char *skip(const char *in) {while (in && (unsigned char)*in<=32) in++; return in;}
+

+
// Parse an object - create a new root, and populate.
+
cJSON *cJSON_Parse(const char *value)
+
{
+
	cJSON *c=cJSON_New_Item();
+
	if (!c) return 0;       /* memory fail */
+

+
	if (!parse_value(c,skip(value))) {cJSON_Delete(c);return 0;}
+
	return c;
+
}
+

+
// Render a cJSON item/entity/structure to text.
+
char *cJSON_Print(cJSON *item)				{return print_value(item,0,1);}
+
char *cJSON_PrintUnformatted(cJSON *item)	{return print_value(item,0,0);}
+

+
// Parser core - when encountering text, process appropriately.
+
static const char *parse_value(cJSON *item,const char *value)
+
{
+
	if (!value)						return 0;	// Fail on null.
+
	if (!strncmp(value,"null",4))	{ item->type=cJSON_NULL;  return value+4; }
+
	if (!strncmp(value,"false",5))	{ item->type=cJSON_False; return value+5; }
+
	if (!strncmp(value,"true",4))	{ item->type=cJSON_True; item->valueint=1;	return value+4; }
+
	if (*value=='\"')				{ return parse_string(item,value); }
+
	if (*value=='-' || (*value>='0' && *value<='9'))	{ return parse_number(item,value); }
+
	if (*value=='[')				{ return parse_array(item,value); }
+
	if (*value=='{')				{ return parse_object(item,value); }
+

+
	return 0;	// failure.
+
}
+

+
// Render a value to text.
+
static char *print_value(cJSON *item,int depth,int fmt)
+
{
+
	char *out=0;
+
	if (!item) return 0;
+
	switch ((item->type)&255)
+
	{
+
		case cJSON_NULL:	out=cJSON_strdup("null");	break;
+
		case cJSON_False:	out=cJSON_strdup("false");break;
+
		case cJSON_True:	out=cJSON_strdup("true"); break;
+
		case cJSON_Number:	out=print_number(item);break;
+
		case cJSON_String:	out=print_string(item);break;
+
		case cJSON_Array:	out=print_array(item,depth,fmt);break;
+
		case cJSON_Object:	out=print_object(item,depth,fmt);break;
+
	}
+
	return out;
+
}
+

+
// Build an array from input text.
+
static const char *parse_array(cJSON *item,const char *value)
+
{
+
	cJSON *child;
+
	if (*value!='[')	return 0;	// not an array!
+

+
	item->type=cJSON_Array;
+
	value=skip(value+1);
+
	if (*value==']') return value+1;	// empty array.
+

+
	item->child=child=cJSON_New_Item();
+
	if (!item->child) return 0;		 // memory fail
+
	value=skip(parse_value(child,skip(value)));	// skip any spacing, get the value.
+
	if (!value) return 0;
+

+
	while (*value==',')
+
	{
+
		cJSON *new_item;
+
		if (!(new_item=cJSON_New_Item())) return 0; 	// memory fail
+
		child->next=new_item;new_item->prev=child;child=new_item;
+
		value=skip(parse_value(child,skip(value+1)));
+
		if (!value) return 0;	// memory fail
+
	}
+

+
	if (*value==']') return value+1;	// end of array
+
	return 0;	// malformed.
+
}
+

+
// Render an array to text
+
static char *print_array(cJSON *item,int depth,int fmt)
+
{
+
	char **entries;
+
	char *out=0,*ptr,*ret;int len=5;
+
	cJSON *child=item->child;
+
	int numentries=0,i=0,fail=0;
+
	
+
	// How many entries in the array?
+
	while (child) numentries++,child=child->next;
+
	// Allocate an array to hold the values for each
+
	entries=(char**)cJSON_malloc(numentries*sizeof(char*));
+
	if (!entries) return 0;
+
	memset(entries,0,numentries*sizeof(char*));
+
	// Retrieve all the results:
+
	child=item->child;
+
	while (child && !fail)
+
	{
+
		ret=print_value(child,depth+1,fmt);
+
		entries[i++]=ret;
+
		if (ret) len+=strlen(ret)+2+(fmt?1:0); else fail=1;
+
		child=child->next;
+
	}
+
	
+
	// If we didn't fail, try to malloc the output string
+
	if (!fail) out=cJSON_malloc(len);
+
	// If that fails, we fail.
+
	if (!out) fail=1;
+

+
	// Handle failure.
+
	if (fail)
+
	{
+
		for (i=0;i<numentries;i++) if (entries[i]) cJSON_free(entries[i]);
+
		cJSON_free(entries);
+
		return 0;
+
	}
+
	
+
	// Compose the output array.
+
	*out='[';
+
	ptr=out+1;*ptr=0;
+
	for (i=0;i<numentries;i++)
+
	{
+
		strcpy(ptr,entries[i]);ptr+=strlen(entries[i]);
+
		if (i!=numentries-1) {*ptr++=',';if(fmt)*ptr++=' ';*ptr=0;}
+
		cJSON_free(entries[i]);
+
	}
+
	cJSON_free(entries);
+
	*ptr++=']';*ptr++=0;
+
	return out;	
+
}
+

+
// Build an object from the text.
+
static const char *parse_object(cJSON *item,const char *value)
+
{
+
	cJSON *child;
+
	if (*value!='{')	return 0;	// not an object!
+
	
+
	item->type=cJSON_Object;
+
	value=skip(value+1);
+
	if (*value=='}') return value+1;	// empty array.
+
	
+
	item->child=child=cJSON_New_Item();
+
	value=skip(parse_string(child,skip(value)));
+
	if (!value) return 0;
+
	child->string=child->valuestring;child->valuestring=0;
+
	if (*value!=':') return 0;	// fail!
+
	value=skip(parse_value(child,skip(value+1)));	// skip any spacing, get the value.
+
	if (!value) return 0;
+
	
+
	while (*value==',')
+
	{
+
		cJSON *new_item;
+
		if (!(new_item=cJSON_New_Item()))	return 0; // memory fail
+
		child->next=new_item;new_item->prev=child;child=new_item;
+
		value=skip(parse_string(child,skip(value+1)));
+
		if (!value) return 0;
+
		child->string=child->valuestring;child->valuestring=0;
+
		if (*value!=':') return 0;	// fail!
+
		value=skip(parse_value(child,skip(value+1)));	// skip any spacing, get the value.		
+
		if (!value) return 0;
+
	}
+
	
+
	if (*value=='}') return value+1;	// end of array
+
	return 0;	// malformed.	
+
}
+

+
// Render an object to text.
+
static char *print_object(cJSON *item,int depth,int fmt)
+
{
+
	char **entries=0,**names=0;
+
	char *out=0,*ptr,*ret,*str;int len=7,i=0,j;
+
	cJSON *child=item->child;
+
	int numentries=0,fail=0;
+
	// Count the number of entries.
+
	while (child) numentries++,child=child->next;
+
	// Allocate space for the names and the objects
+
	entries=(char**)cJSON_malloc(numentries*sizeof(char*));
+
	if (!entries) return 0;
+
	names=(char**)cJSON_malloc(numentries*sizeof(char*));
+
	if (!names) {cJSON_free(entries);return 0;}
+
	memset(entries,0,sizeof(char*)*numentries);
+
	memset(names,0,sizeof(char*)*numentries);
+

+
	// Collect all the results into our arrays:
+
	child=item->child;depth++;if (fmt) len+=depth;
+
	while (child)
+
	{
+
		names[i]=str=print_string_ptr(child->string);
+
		entries[i++]=ret=print_value(child,depth,fmt);
+
		if (str && ret) len+=strlen(ret)+strlen(str)+2+(fmt?2+depth:0); else fail=1;
+
		child=child->next;
+
	}
+
	
+
	// Try to allocate the output string
+
	if (!fail) out=(char*)cJSON_malloc(len);
+
	if (!out) fail=1;
+

+
	// Handle failure
+
	if (fail)
+
	{
+
		for (i=0;i<numentries;i++) {if (names[i]) free(names[i]);if (entries[i]) free(entries[i]);}
+
		free(names);free(entries);
+
		return 0;
+
	}
+
	
+
	// Compose the output:
+
	*out='{';ptr=out+1;if (fmt)*ptr++='\n';*ptr=0;
+
	for (i=0;i<numentries;i++)
+
	{
+
		if (fmt) for (j=0;j<depth;j++) *ptr++='\t';
+
		strcpy(ptr,names[i]);ptr+=strlen(names[i]);
+
		*ptr++=':';if (fmt) *ptr++='\t';
+
		strcpy(ptr,entries[i]);ptr+=strlen(entries[i]);
+
		if (i!=numentries-1) *ptr++=',';
+
		if (fmt) *ptr++='\n';*ptr=0;
+
		cJSON_free(names[i]);cJSON_free(entries[i]);
+
	}
+
	
+
	cJSON_free(names);cJSON_free(entries);
+
	if (fmt) for (i=0;i<depth-1;i++) *ptr++='\t';
+
	*ptr++='}';*ptr++=0;
+
	return out;	
+
}
+

+
// Get Array size/item / object item.
+
int    cJSON_GetArraySize(cJSON *array)							{cJSON *c=array->child;int i=0;while(c)i++,c=c->next;return i;}
+
cJSON *cJSON_GetArrayItem(cJSON *array,int item)				{cJSON *c=array->child;  while (c && item>0) item--,c=c->next; return c;}
+
cJSON *cJSON_GetObjectItem(cJSON *object,const char *string)	{cJSON *c=object->child; while (c && cJSON_strcasecmp(c->string,string)) c=c->next; return c;}
+

+
// Utility for array list handling.
+
static void suffix_object(cJSON *prev,cJSON *item) {prev->next=item;item->prev=prev;}
+
// Utility for handling references.
+
static cJSON *create_reference(cJSON *item) {cJSON *ref=cJSON_New_Item();memcpy(ref,item,sizeof(cJSON));ref->string=0;ref->type|=cJSON_IsReference;ref->next=ref->prev=0;return ref;}
+

+
// Add item to array/object.
+
void   cJSON_AddItemToArray(cJSON *array, cJSON *item)						{cJSON *c=array->child;if (!c) {array->child=item;} else {while (c && c->next) c=c->next; suffix_object(c,item);}}
+
void   cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item)	{if (item->string) cJSON_free(item->string);item->string=cJSON_strdup(string);cJSON_AddItemToArray(object,item);}
+
void	cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item)						{cJSON_AddItemToArray(array,create_reference(item));}
+
void	cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item)	{cJSON_AddItemToObject(object,string,create_reference(item));}
+

+
cJSON *cJSON_DetachItemFromArray(cJSON *array,int which)			{cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return 0;
+
	if (c->prev) c->prev->next=c->next;if (c->next) c->next->prev=c->prev;if (c==array->child) array->child=c->next;c->prev=c->next=0;return c;}
+
void   cJSON_DeleteItemFromArray(cJSON *array,int which)			{cJSON_Delete(cJSON_DetachItemFromArray(array,which));}
+
cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string) {int i=0;cJSON *c=object->child;while (c && cJSON_strcasecmp(c->string,string)) i++,c=c->next;if (c) return cJSON_DetachItemFromArray(object,i);return 0;}
+
void   cJSON_DeleteItemFromObject(cJSON *object,const char *string) {cJSON_Delete(cJSON_DetachItemFromObject(object,string));}
+

+
// Replace array/object items with new ones.
+
void   cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem)		{cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return;
+
	newitem->next=c->next;newitem->prev=c->prev;if (newitem->next) newitem->next->prev=newitem;
+
	if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;c->next=c->prev=0;cJSON_Delete(c);}
+
void   cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem){int i=0;cJSON *c=object->child;while(c && cJSON_strcasecmp(c->string,string))i++,c=c->next;if(c){newitem->string=cJSON_strdup(string);cJSON_ReplaceItemInArray(object,i,newitem);}}
+

+
// Create basic types:
+
cJSON *cJSON_CreateNull()						{cJSON *item=cJSON_New_Item();item->type=cJSON_NULL;return item;}
+
cJSON *cJSON_CreateTrue()						{cJSON *item=cJSON_New_Item();item->type=cJSON_True;return item;}
+
cJSON *cJSON_CreateFalse()						{cJSON *item=cJSON_New_Item();item->type=cJSON_False;return item;}
+
cJSON *cJSON_CreateNumber(double num)			{cJSON *item=cJSON_New_Item();item->type=cJSON_Number;item->valuedouble=num;item->valueint=(int)num;return item;}
+
cJSON *cJSON_CreateString(const char *string)	{cJSON *item=cJSON_New_Item();item->type=cJSON_String;item->valuestring=cJSON_strdup(string);return item;}
+
cJSON *cJSON_CreateArray()						{cJSON *item=cJSON_New_Item();item->type=cJSON_Array;return item;}
+
cJSON *cJSON_CreateObject()						{cJSON *item=cJSON_New_Item();item->type=cJSON_Object;return item;}
+

+
// Create Arrays:
+
cJSON *cJSON_CreateIntArray(int *numbers,int count)				{int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
+
cJSON *cJSON_CreateFloatArray(float *numbers,int count)			{int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
+
cJSON *cJSON_CreateDoubleArray(double *numbers,int count)		{int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
+
cJSON *cJSON_CreateStringArray(const char **strings,int count)	{int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;i<count;i++){n=cJSON_CreateString(strings[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
added external/cjson/cJSON.h
@@ -0,0 +1,123 @@
+
/*
+
  Copyright (c) 2009 Dave Gamble
+
 
+
  Permission is hereby granted, free of charge, to any person obtaining a copy
+
  of this software and associated documentation files (the "Software"), to deal
+
  in the Software without restriction, including without limitation the rights
+
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+
  copies of the Software, and to permit persons to whom the Software is
+
  furnished to do so, subject to the following conditions:
+
 
+
  The above copyright notice and this permission notice shall be included in
+
  all copies or substantial portions of the Software.
+
 
+
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+
  THE SOFTWARE.
+
*/
+

+
#ifndef cJSON__h
+
#define cJSON__h
+

+
#ifdef __cplusplus
+
extern "C"
+
{
+
#endif
+

+
// cJSON Types:
+
#define cJSON_False 0
+
#define cJSON_True 1
+
#define cJSON_NULL 2
+
#define cJSON_Number 3
+
#define cJSON_String 4
+
#define cJSON_Array 5
+
#define cJSON_Object 6
+
	
+
#define cJSON_IsReference 256
+

+
// The cJSON structure:
+
typedef struct cJSON {
+
	struct cJSON *next,*prev;	// next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem
+
	struct cJSON *child;		// An array or object item will have a child pointer pointing to a chain of the items in the array/object.
+

+
	int type;					// The type of the item, as above.
+

+
	char *valuestring;			// The item's string, if type==cJSON_String
+
	int valueint;				// The item's number, if type==cJSON_Number
+
	double valuedouble;			// The item's number, if type==cJSON_Number
+

+
	char *string;				// The item's name string, if this item is the child of, or is in the list of subitems of an object.
+
} cJSON;
+

+
typedef struct cJSON_Hooks {
+
      void *(*malloc_fn)(size_t sz);
+
      void (*free_fn)(void *ptr);
+
} cJSON_Hooks;
+

+
// Supply malloc, realloc and free functions to cJSON
+
extern void cJSON_InitHooks(cJSON_Hooks* hooks);
+

+

+
// Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished.
+
extern cJSON *cJSON_Parse(const char *value);
+
// Render a cJSON entity to text for transfer/storage. Free the char* when finished.
+
extern char  *cJSON_Print(cJSON *item);
+
// Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished.
+
extern char  *cJSON_PrintUnformatted(cJSON *item);
+
// Delete a cJSON entity and all subentities.
+
extern void   cJSON_Delete(cJSON *c);
+

+
// Returns the number of items in an array (or object).
+
extern int	  cJSON_GetArraySize(cJSON *array);
+
// Retrieve item number "item" from array "array". Returns NULL if unsuccessful.
+
extern cJSON *cJSON_GetArrayItem(cJSON *array,int item);
+
// Get item "string" from object. Case insensitive.
+
extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string);
+
	
+
// These calls create a cJSON item of the appropriate type.
+
extern cJSON *cJSON_CreateNull(void);
+
extern cJSON *cJSON_CreateTrue(void);
+
extern cJSON *cJSON_CreateFalse(void);
+
extern cJSON *cJSON_CreateNumber(double num);
+
extern cJSON *cJSON_CreateString(const char *string);
+
extern cJSON *cJSON_CreateArray(void);
+
extern cJSON *cJSON_CreateObject(void);
+

+
// These utilities create an Array of count items.
+
extern cJSON *cJSON_CreateIntArray(int *numbers,int count);
+
extern cJSON *cJSON_CreateFloatArray(float *numbers,int count);
+
extern cJSON *cJSON_CreateDoubleArray(double *numbers,int count);
+
extern cJSON *cJSON_CreateStringArray(const char **strings,int count);
+

+
// Append item to the specified array/object.
+
extern void cJSON_AddItemToArray(cJSON *array, cJSON *item);
+
extern void	cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item);
+
// Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON.
+
extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
+
extern void	cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item);
+

+
// Remove/Detatch items from Arrays/Objects.
+
extern cJSON *cJSON_DetachItemFromArray(cJSON *array,int which);
+
extern void   cJSON_DeleteItemFromArray(cJSON *array,int which);
+
extern cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string);
+
extern void   cJSON_DeleteItemFromObject(cJSON *object,const char *string);
+
	
+
// Update array items.
+
extern void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem);
+
extern void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
+

+
#define cJSON_AddNullToObject(object,name)	cJSON_AddItemToObject(object, name, cJSON_CreateNull())
+
#define cJSON_AddTrueToObject(object,name)	cJSON_AddItemToObject(object, name, cJSON_CreateTrue())
+
#define cJSON_AddFalseToObject(object,name)		cJSON_AddItemToObject(object, name, cJSON_CreateFalse())
+
#define cJSON_AddNumberToObject(object,name,n)	cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n))
+
#define cJSON_AddStringToObject(object,name,s)	cJSON_AddItemToObject(object, name, cJSON_CreateString(s))
+

+
#ifdef __cplusplus
+
}
+
#endif
+

+
#endif
added external/tinycdb/ChangeLog
@@ -0,0 +1,184 @@
+
2006-06-29  Michael Tokarev  <mjt@corpit.ru>
+

+
	* see debian/changelog file for further changes.
+

+
2005-04-18  Michael Tokarev  <mjt@corpit.ru>
+

+
	* move cdb_make_find.c content into cdb_make_put.c
+

+
	* introduce CDB_PUT_REPLACE0 - zerofill old duplicates
+

+
	* allow usage of cdb.h in C++
+

+
2005-04-11  Michael Tokarev  <mjt@corpit.ru>
+

+
	* do not autogenerate files (cdb.h.in, cdb.3.in etc), but
+
	  use real files instead (only substituted VERSION and NAME)
+

+
	* finally fixed the `!fputs()' condition to be `fputs() < 0'
+
	  as it should be in cdb.c (misbehaves on *bsd)
+

+
	* kill cdbi_t usage in cdb_int.h
+

+
	* export _cdb_make_fullwrite() (was ewrite()) and _cdb_make_flush()
+
	  and use them in cdb_make.c as appropriate
+

+
	* almost completely rewrite _cdb_make_find() and friends:
+
	  - _cdb_make_find() now accepts new parameter, remove (bool), which,
+
	    if true, indicates all found records should be deleted from the
+
	    database.
+
	  - Call _cdb_make_find() with remove=0 from cdb_make_exists()
+
	  - Call _cdb_make_find() with appropriate arguments from
+
	    cdb_make_put(), and simplify the latter greatly (was too clumsy
+
	    anyway)
+

+
	* rename `flags' parameter in cdb_make_put() to be `mode' which is
+
	  more appropriate
+

+
	* change #if conditional in nss_cdb.c
+
	  from __GLIBC__ to __GNU_LIBRARY__
+

+
2003-11-04  Michael Tokarev  <mjt@corpit.ru>
+

+
	* added cdb_get() routine: tinycdb officially uses mmap.
+

+
	* added cdb_{get,read}{data,key}() macros to read and get
+
	  current data and key.
+

+
	* fixed bug in cdb_seek() - incorrect wrap, sometimes
+
	  cdb_seek()+cdb_bread() may return EIO instead of finding
+
	  correct record.
+
	
+
	* added some tweaks to Makefile to build position-independent
+
	  libcdb_pic.a and shared libcdb.so libraries.  Note that
+
	  using libcdb as shared library is probably not a good idea,
+
	  due to tiny size of the library.
+

+
	* added initial nss_cdb module.  Still not well-tested.
+
	  Probably will not build on non-GNU system.
+

+
	* adjusted tests.{ok,sh} for latest cdb utility modifications
+
	  (-a mode in query by default)
+

+
	* Victor Porton (porton at ex-code.com) provided a patch
+
	  to allow tinycdb to be built on win32 platform (cdb_init.c).
+
	  Completely untested.
+

+
2003-08-13  Michael Tokarev  <mjt@corpit.ru>
+

+
	* s/cdbi_t/unsigned/g.  No need to keep this type.
+

+
	* changed usage of cdb_findnext(): one need to pass
+
	  pointer to cdb structure to cdb_findnext() now,
+
	  and should use cdb_datapos(struct cdb_find *)
+
	  instead of cdb_datapos(struct cdb *)
+

+
	* added cdb_seqinit() and cdb_seqnext() routines for sequential
+
	  record enumeration
+

+
	* addded cdb_dend to the cdb structure: end of data
+
	  position.  Use that in cdb_seq*().
+

+
	* more strict checking: ensure data is within data section,
+
	  and hash tables are within hash section of a file.
+

+
	* cdb_make.c (cdb_make_start): zerofill cdb_make structure
+
	  to shut valgrind up (writing uninitialized data to file)
+
	
+
	* cdb.c (cmode): always open file in RDWR mode to allow
+
	  duplicate key detection
+

+
2002-12-08  Michael Tokarev  <mjt+cdb@corpit.ru>
+

+
	* version 0.73
+
	* de-Debianization.  Oh well... ;)
+
	* no code changes, just like in 0.72
+

+
2002-10-13  Michael Tokarev  <mjt+cdb@corpit.ru>
+

+
	* version 0.72
+

+
	* cleaned up debian packaging and made it actually work
+

+
	* no code changes
+

+
2002-07-22  Michael Tokarev <mjt+cdb@corpit.ru>
+

+
	* version 0.71
+

+
	* rearranged object files to not depend on ranlib on
+
	  systems that requires it (i.e. OpenBSD)
+

+
	* use ranlib but mark it's possible error as non-fatal
+

+
2001-12-10  Michael Tokarev <mjt+cdb@corpit.ru>
+

+
	* version 0.7a
+

+
	* converted to CVS, added two missing #include <stdlib.h> for
+
	  malloc declaration and spec target to the Makefile
+

+
2001-10-14  Michael Tokarev <mjt+cdb@corpit.ru>
+

+
	* version 0.7
+

+
	* added cdb_seek() and cdb_bread() routines as found
+
	  in freecdb/cdb-0.64
+

+
2001-07-26  Michael Tokarev <mjt+cdb@corpit.ru>
+

+
	* version 0.6
+

+
	* added another option, CDB_PUT_WARN, to cdb_make_put's flags
+
	  (to allow adding unconditionally but still warn about dups),
+
	  now cdb_make_put seems to be logically complete.
+

+
	* added and documented -r and -u options for cdb(1) command,
+
	  and made them consistent with -w and -e also.
+

+
	* reorganized cdb(1) manpage and added changes made to cdb
+
	  command.
+

+
	* added version references to manpages (and make them autogenerated
+
	  to simplify maintenance).
+

+
	* added cdb(5) manpage describing CDB file format.
+

+
2001-07-25  Michael Tokarev <mjt+cdb@corpit.ru>
+

+
	* version 0.5
+

+
	* added missing #include <sys/types.h> in cdb_init.c, thanks to
+
	  ppetru@ppetru.net (Petru Paler)
+

+
	* removed usage of pread() in cdb_make_find() and friends,
+
	  suggested by Liviu Daia <Liviu.Daia@imar.ro>
+

+
	* autogenerate tinycdb.spec file from template and debian/changelog
+

+
	* autogenerate cdb.h from cdb.h.in (substituting version)
+

+
2001-06-29  Michael Tokarev <mjt+cdb@corpit.ru>
+

+
	* version 0.4
+

+
	* added cdb_make_put() routine to conditionnaly add a record
+

+
	* split cdb library to more files (finer granularity)
+

+
	* added cdb_findinit() and cdb_findnext() routines
+

+
	* renamed cdbtool to cdb
+

+
	* simplified cdb utility (dropped various format spec, changed
+
	  options parsing) and a manpage
+

+
	* added small note and copyright to every file in package
+

+
	* added some testsuite (make test)
+

+
2001-05-27  Michael Tokarev <mjt+cdb@corpit.ru>
+

+
	* version 0.3
+

+
	* Initial Release.
added external/tinycdb/Makefile
@@ -0,0 +1,13 @@
+
LIB=	cdb
+
INTERNALLIB=
+
SRCS= cdb_init.c cdb_find.c cdb_findnext.c cdb_seq.c cdb_seek.c \
+
      cdb_unpack.c \
+
      cdb_make_add.c cdb_make_put.c cdb_make.c cdb_hash.c
+

+
CFLAGS+=-fPIC
+
WARNS?=6
+
WFORMAT?=1
+

+
NO_MAN=true
+

+
.include <bsd.lib.mk>
added external/tinycdb/Makefile.orig
@@ -0,0 +1,174 @@
+
#! /usr/bin/make -rf
+
# $Id: Makefile,v 1.28 2009-01-31 17:12:21 mjt Exp $
+
# make file for tinycdb package
+
#
+
# This file is a part of tinycdb package by Michael Tokarev, mjt@corpit.ru.
+
# Public domain.
+

+
VERSION = 0.77
+

+
prefix=/usr/local
+
exec_prefix=$(prefix)
+
bindir=$(exec_prefix)/bin
+
libdir=$(exec_prefix)/lib
+
syslibdir=$(libdir)
+
sysconfdir=/etc
+
includedir=$(prefix)/include
+
mandir=$(prefix)/man
+
NSSCDB_DIR = $(sysconfdir)
+
DESTDIR=
+

+
CC = cc
+
CFLAGS = -O
+

+
AR = ar
+
ARFLAGS = rv
+
RANLIB = ranlib
+

+
NSS_CDB = libnss_cdb.so.2
+
LIBBASE = libcdb
+
LIB = $(LIBBASE).a
+
PICLIB = $(LIBBASE)_pic.a
+
SHAREDLIB = $(LIBBASE).so.1
+
SOLIB = $(LIBBASE).so
+
CDB_USELIB = $(LIB)
+
NSS_USELIB = $(PICLIB)
+
LIBMAP = $(LIBBASE).map
+
INSTALLPROG = cdb
+

+
# The following assumes GNU CC/LD -
+
# used for building shared libraries only
+
CFLAGS_PIC = -fPIC
+
CFLAGS_SHARED = -shared
+
CFLAGS_SONAME = -Wl,--soname=
+
CFLAGS_VSCRIPT = -Wl,--version-script=
+

+
CP = cp
+

+
LIB_SRCS = cdb_init.c cdb_find.c cdb_findnext.c cdb_seq.c cdb_seek.c \
+
 cdb_unpack.c \
+
 cdb_make_add.c cdb_make_put.c cdb_make.c cdb_hash.c
+
NSS_SRCS = nss_cdb.c nss_cdb-passwd.c nss_cdb-group.c nss_cdb-spwd.c
+
NSSMAP = nss_cdb.map
+

+
DISTFILES = Makefile cdb.h cdb_int.h $(LIB_SRCS) cdb.c \
+
 $(NSS_SRCS) nss_cdb.h nss_cdb-Makefile \
+
 cdb.3 cdb.1 cdb.5 \
+
 tinycdb.spec tests.sh tests.ok \
+
 $(LIBMAP) $(NSSMAP) \
+
 ChangeLog NEWS
+
DEBIANFILES = debian/control debian/rules debian/copyright debian/changelog
+

+
all: static
+
static: staticlib cdb
+
staticlib: $(LIB)
+
nss: $(NSS_CDB)
+
piclib: $(PICLIB)
+
sharedlib: $(SHAREDLIB)
+
shared: sharedlib cdb-shared
+

+
LIB_OBJS = $(LIB_SRCS:.c=.o)
+
LIB_OBJS_PIC = $(LIB_SRCS:.c=.lo)
+
NSS_OBJS = $(NSS_SRCS:.c=.lo)
+

+
$(LIB): $(LIB_OBJS)
+
	-rm -f $@
+
	$(AR) $(ARFLAGS) $@ $(LIB_OBJS)
+
	-$(RANLIB) $@
+

+
$(PICLIB): $(LIB_OBJS_PIC)
+
	-rm -f $@
+
	$(AR) $(ARFLAGS) $@ $(LIB_OBJS_PIC)
+
	-$(RANLIB) $@
+

+
$(SHAREDLIB): $(LIB_OBJS_PIC) $(LIBMAP)
+
	-rm -f $(SOLIB)
+
	ln -s $@ $(SOLIB)
+
	$(CC) $(CFLAGS) $(CFLAGS_SHARED) -o $@ \
+
	 $(CFLAGS_SONAME)$(SHAREDLIB) $(CFLAGS_VSCRIPT)$(LIBMAP) \
+
	 $(LIB_OBJS_PIC)
+

+
cdb: cdb.o $(CDB_USELIB)
+
	$(CC) $(CFLAGS) -o $@ cdb.o $(CDB_USELIB)
+
cdb-shared: cdb.o $(SHAREDLIB)
+
	$(CC) $(CFLAGS) -o $@ cdb.o $(SHAREDLIB)
+

+
$(NSS_CDB): $(NSS_OBJS) $(NSS_USELIB) $(NSSMAP)
+
	$(CC) $(CFLAGS) $(CFLAGS_SHARED) -o $@ \
+
	 $(CFLAGS_SONAME)$@ $(CFLAGS_VSCRIPT)$(NSSMAP) \
+
	 $(NSS_OBJS) $(NSS_USELIB)
+

+
.SUFFIXES:
+
.SUFFIXES: .c .o .lo
+

+
.c.o:
+
	$(CC) $(CFLAGS) -c $<
+
.c.lo:
+
	$(CC) $(CFLAGS) $(CFLAGS_PIC) -c -o $@ -DNSSCDB_DIR=\"$(NSSCDB_DIR)\" $<
+

+
cdb.o: cdb.h
+
$(LIB_OBJS) $(LIB_OBJS_PIC): cdb_int.h cdb.h
+
$(NSS_OBJS): nss_cdb.h cdb.h
+

+
clean:
+
	-rm -f *.o *.lo core *~ tests.out tests-shared.ok
+
realclean distclean:
+
	-rm -f *.o *.lo core *~ $(LIBBASE)[._][aps]* $(NSS_CDB)* cdb cdb-shared
+

+
test tests check: cdb
+
	sh ./tests.sh ./cdb > tests.out 2>&1
+
	diff tests.ok tests.out
+
	@echo All tests passed
+
test-shared tests-shared check-shared: cdb-shared
+
	sed 's/^cdb: /cdb-shared: /' <tests.ok >tests-shared.ok
+
	LD_LIBRARY_PATH=. sh ./tests.sh ./cdb-shared > tests.out 2>&1
+
	diff tests-shared.ok tests.out
+
	rm -f tests-shared.ok
+
	@echo All tests passed
+

+
do_install = \
+
 while [ "$$1" ] ; do \
+
   if [ .$$4 = .- ]; then f=$$1; else f=$$4; fi; \
+
   d=$(DESTDIR)$$3 ; echo installing $$1 to $$d/$$f; \
+
   [ -d $$d ] || mkdir -p $$d || exit 1 ; \
+
   $(CP) $$1 $$d/$$f || exit 1; \
+
   chmod 0$$2 $$d/$$f || exit 1; \
+
   shift 4; \
+
 done
+

+
install-all: all $(INSTALLPROG)
+
	set -- \
+
	 cdb.h 644 $(includedir) - \
+
	 cdb.3 644 $(mandir)/man3 - \
+
	 cdb.1 644 $(mandir)/man1 - \
+
	 cdb.5 644 $(mandir)/man5 - \
+
	 $(INSTALLPROG) 755 $(bindir) cdb \
+
	 libcdb.a 644 $(libdir) - \
+
	 ; \
+
	$(do_install)
+
install-nss: nss
+
	@set -- $(NSS_CDB) 644 $(syslibdir) - \
+
	        nss_cdb-Makefile 644 $(sysconfdir) cdb-Makefile ; \
+
	$(do_install)
+
install-sharedlib: sharedlib
+
	@set -- $(SHAREDLIB) 644 $(libdir) - ; \
+
	$(do_install) ; \
+
	ln -sf $(SHAREDLIB) $(DESTDIR)$(libdir)/$(LIBBASE).so
+
install-piclib: piclib
+
	@set -- $(PICLIB) 644 $(libdir) - ; \
+
	$(do_install)
+
install: install-all
+

+
DNAME = tinycdb-$(VERSION)
+
dist: $(DNAME).tar.gz
+
$(DNAME).tar.gz: $(DISTFILES) $(DEBIANFILES)
+
	mkdir $(DNAME) $(DNAME)/debian
+
	ln $(DISTFILES) $(DNAME)/
+
	ln $(DEBIANFILES) $(DNAME)/debian/
+
	tar cfz $@ $(DNAME)
+
	rm -fr $(DNAME)
+

+
.PHONY: all clean realclean dist spec
+
.PHONY: test tests check test-shared tests-shared check-shared
+
.PHONY: static staticlib shared sharedlib nss piclib
+
.PHONY: install install-all install-sharedlib install-piclib install-nss
added external/tinycdb/NEWS
@@ -0,0 +1,79 @@
+
$Id: NEWS,v 1.6 2009-01-31 17:12:21 mjt Exp $
+
User-visible news.  Latest at the top.
+

+
tinycdb-0.77
+

+
 - bugfix release: manpage typos, portability fixes and the like
+

+
 - bugfix: improper logic in EINTR handling in _cdb_make_full_write
+
   routine which may lead to corruped .cdb file.
+

+
tinycdb-0.76
+

+
 - new cdb utility option: -p permissions, to specify permission
+
   bits for the newly created database file.
+

+
 - cdb utility, when creating the database, does unlink() of the
+
   (temp) db file and when opens it with O_CREAT|O_EXCL, instead
+
   of previous O_CREAT|O_TRUNC w/o unlink() behaviour, and uses
+
   O_NOFOLLOW flag if available.  This avoids any possible symlink
+
   race conditions, but is somewhat incompatible with previous
+
   versions, where it was possible to create temp file beforehand
+
   with proper permissions.  Together with new -p option (above),
+
   this isn't that big change.
+

+
 - cdb utility now allows to omit temp file usage (with final rename())
+
   when creating the database, by specifying -t- (`-' as temp file name)
+
   (or -t the_same_name_as_the_db_file).  Useful if the utility is called
+
   from a script which does its own rename afterwards.
+

+
 - alot of minor code changes to make modern compilers happy (mostly
+
   signed char vs unsigned char "fixes").  Including additions of
+
   `unsigned' into common structure definitions in cdb.h - the API
+
   stays compatible still.
+

+
 - several (spelling and other) fixes for manpages.
+

+
 - tinycdb is, once again, maintained as a native Debian package.
+
   Debian package has been split into 4 parts: libcdb1, libcdb-dev,
+
   tinycdb (the utility) and libnss_cdb.  RPM .spec file now builds
+
   two packages as well: tinycdb (includes shared library, the utility,
+
   and nss_cdb module) and tinycdb-devel (development files).
+

+
tinycdb-0.75 (2005-04-11)
+

+
 - make cdb_make_put(CDB_PUT_REPLACE) to actually *remove*
+
   all (was only first previously) matching records, by
+
   rewriting the file.
+

+
 - new mode CDB_PUT_REPLACE0, which zeroes out old duplicate
+
   records
+

+
 - fixed fputs() == NULL condition in cdb.c, finally
+
   (should be < 0, not == NULL)
+

+
tinycdb-0.74 (2003-11-04)
+

+
 - reworked cdb utility, see manpage for details.  cdb -q now
+
   prints all matching records by default (incompat change),
+
   use cdb -q -n XX to return only XXth record if any.
+
   -m works with -q.
+

+
 - there are several new routines (and macros) in library.
+
   - cdb_seqinit() and cdb_seqstart() to fetch all records
+
   - cdb_get() to get a pointer to data in internal buffer
+
   - cdb_{get,read}{data,key}()
+

+
 - cdbi_t gone (but still defined).  Use unsigned instead.
+

+
 - cdb_findnext() changed
+

+
 - fixed a bug in cdb_seek() (EIO instead of getting valid record)
+

+
 - added nss_cdb (for passwd, group and shadow databases only)
+

+
 - Makefile targets to build PIC code (libcdb_pic.a, required for
+
   nss_cdb) and shared library (probably not a good idea)
+

+
 - Modifications to allow tinycdb to compile under win32,
+
   by Victor Porton (porton at ex-code.com).
added external/tinycdb/cdb.1
@@ -0,0 +1,198 @@
+
.\" $Id: cdb.1,v 1.12 2009-01-31 17:12:22 mjt Exp $
+
.\" cdb command tool manpage
+
.\"
+
.\" This file is a part of tinycdb package by Michael Tokarev, mjt@corpit.ru.
+
.\" Public domain.
+
.\"
+
.TH cdb 1 "Jan 2009"
+
.SH NAME
+
cdb \- Constant DataBase manipulation tool
+
.SH SYNOPSYS
+
\fBcdb\fR \-q [\-m] [\-n \fInum\fR] \fIdbname\fR \fIkey\fR
+
.br
+
\fBcdb\fR \-d [\-m] [\fIdbname\fR|\-]
+
.br
+
\fBcdb\fR \-l [\-m] [\fIdbname\fR|\-]
+
.br
+
\fBcdb\fR \-s [\fIdbname\fR|\-]
+
.br
+
\fBcdb\fR \-c [\-m] [\-t \fItmpname\fR|\-] [\-p \fIperms\fR] [\-weru0] \fIdbname\fR [\fIinfile\fR...]
+

+
.SH DESCRIPTION
+

+
\fBcdb\fR used to query, dump, list, analyze or create CDB (Constant
+
DataBase) files.  Format of cdb described in \fIcdb\fR(5) manpage.
+
This manual page corresponds to version \fB0.77\fR of \fBtinycdb\fR
+
package.
+

+
.SS Query
+

+
\fBcdb \-q\fR finds given \fIkey\fR in a given \fIdbname\fR
+
cdb file, and writes associated value to standard output if found (and
+
exits with zero), or exits with non\-zero if not found.  \fIdbname\fR must
+
be seekable file, and stdin can not be used as input.
+
By default, \fBcdb\fR will print \fIall\fR records found.
+
Options recognized in query mode:
+

+
.IP \fB\-n\fInum\fR
+
causes \fBcdb\fR to find and write a record with a given number \fInum\fR
+
starting with 1 \(em when there are many records with a given key.
+

+
.IP \fB\-m\fR
+
newline will be added after every value printed.  By default, multiple
+
values will be written without any delimiter.
+

+
.SS "Dump/List"
+

+
\fBcdb \-d\fR dumps contents, and \fBcdb \-l\fR lists keys
+
of \fIcdbfile\fR (or standard input if not specified) to standard
+
output, in format controlled by presence of \fB\-m\fR option.
+
See subsection "Formats" below.  Output from \fBcdb \-d\fR
+
can be used as an input for \fBcdb \-c\fR.
+

+
.SS Create
+

+
Cdb database created in two stages: temporary database is created,
+
and after it is complete, it gets atomically renamed to permanent
+
place.  This avoids requirements for locking between readers and
+
writers (or creaters).  \fBcdb \-c\fR will attempt to create
+
cdb in file \fItmpname\fR (or \fIdbname\fR with ".tmp" appended
+
if no \-t option given) and then rename it to \fIdbname\fR.  It
+
will read supplied \fIinfile\fRs (or standard input if none specified).
+
Options recognized in create mode:
+

+
.IP "\fB\-t \fItmpname\fR"
+
use given \fItmpname\fR as temporary file.  Defaults to
+
\fIdbname\fR.tmp (i.e. with output file with .tmp added).
+
Note \fItmpname\fR must be in the same filesystem as output file, as
+
.B cdb
+
uses
+
.IR rename (2)
+
to finalize the database creation procedure.
+
If \fItmpname\fR is a single dash (\-), no temp file will be created,
+
database will be built in\-place.  This mode is useful when the final
+
renaming is done by the caller.
+

+
.IP "\fB\-p \fIperms\fR"
+
permissions for the newly created file (usually an octal number, like 0644).
+
By default the permissions are 0666 (with current process umask applied).
+
If this option is specified, current umask value has no effect.
+

+
.IP \fB\-w\fR
+
warn about duplicate keys.
+

+
.IP \fB\-e\fR
+
abort on duplicate keys (implies \-w).
+

+
.IP \fB\-r\fR
+
replace existing key with new one in case of duplicate.
+
This may require database file rewrite to remove old
+
records, and can be slow.
+

+
.IP \fB\-0\fR
+
zero-fill existing records when duplicate records are
+
added.  This is faster than \fB\-r\fR, but leaves extra
+
zeros in the database file in case of duplicates.
+

+
.IP \fB\-u\fR
+
do not add duplicate records.
+

+
.IP \fB\-m\fR
+
interpret input as a sequence of lines, one record per line,
+
with value separated from a key by space or tab characters,
+
instead of native cdb format (see "Input/Output Format" below).
+

+
.PP
+
Note that using any option that requires duplicate checking will
+
slow creation process \fIsignificantly\fR, especially for large
+
databases.
+

+
.SS Statistics
+

+
\fBcdb \-s\fR will analyze \fIdbfile\fR and print summary to
+
standard output.  Statistics include: total number of rows in
+
a file, minimum, average and maximum key and value lengths,
+
hash tables (max 256) and entries used, number of hash collisions
+
(that is, more than one key point to the same hash table entry),
+
minimum, average and maximum hash table size (of non-empty tables),
+
and number of keys that sits at 10 different distances from
+
it's calculated hash table index \(em keys in distance 0 requires
+
only one hash table lookup, 1 \(em two and so on; more keys at
+
greater distance means slower database search.
+

+
.SS "Input/Output Format"
+

+
By default, \fBcdb\fR expects (for create operation) or writes
+
(for dump/list) native cdb format data.  Cdb native format is
+
a sequence of records in a form:
+
.br
+
    +\fIklen\fR,\fIvlen\fR:\fIkey\fR\->\fIval\fR\\n
+
.br
+
where "+", ",", ":", "\-", ">" and "\\n" (newline) are literal characters,
+
\fIklen\fR and \fIvlen\fR are length of key and value as decimal numbers,
+
and \fIkey\fR and \fIval\fR are key and value themselves.  Series of
+
records terminated by an empty line.  This is the only format where
+
key and value may contain any character including newline, zero (\\0)
+
and so on.
+

+
When \fB\-l\fR option requested (list keys mode), \fBcdb\fR will produce
+
slightly modified output in a form:
+
.br
+
    +\fIklen\fR:\fIkey\fR\\n
+
.br
+
(note \fIvlen\fR and \fIval\fR are omitted, together with surrounding
+
delimiters).
+

+
If \fB\-m\fR option is given, \fBcdb\fR will expect or produce one line
+
for every record (newline is a record delimiter), and every line should
+
contain optional whitespace, key, whitespace and value up to end of line.
+
Lines started with hash character (#) and empty lines are ignored.
+
This is the same format as \fBmkmap\fR(1) utility expects.
+

+
.SH "OPTIONS SUMMARY"
+

+
Here is a short summary of all options accepted by \fBcdb\fR utility:
+

+
.IP \fB\-0\fR
+
zero-fill duplicate records in create (\fB\-c\fR) mode.
+
.IP \fB\-c\fR
+
create mode.
+
.IP \fB\-d\fR
+
dump mode.
+
.IP \fB\-e\fR
+
abort (error) on duplicate key in create (\fB\-c\fR) mode.
+
.IP \fB\-h\fR
+
print short help and exit.
+
.IP \fB\-l\fR
+
list mode.
+
.IP \fB\-m\fR
+
input or output is in "map" format, not in native cdb format.  In query
+
mode, add a newline after every value written.
+
.IP \fB\-n\fInum\fR
+
find and print \fInum\fRth record in query (\fB\-q\fR) mode.
+
.IP \fB\-q\fR
+
query mode.
+
.IP \fB\-r\fR
+
replace duplicate keys in create (\fB\-c\fR) mode.
+
.IP \fB\-s\fR
+
statistics mode.
+
.IP "\fB\-t\fR \fItempfile\fR"
+
specify temporary file when creating (\fB\-c\fR) cdb file (use single dash
+
(\-) as \fItempfile\fR to stop using temp file).
+
.IP \fB\-u\fR
+
do not insert duplicate keys (unique) in create (\fB\-c\fR) mode.
+
.IP \fB\-w\fR
+
warn about duplicate keys in create (\fB\-c\fR) mode.
+

+
.SH AUTHOR
+

+
The \fBtinycdb\fR package written by Michael Tokarev <mjt@corpit.ru>,
+
based on ideas and shares file format with original cdb library by
+
Dan Bernstein.
+

+
.SH "SEE ALSO"
+
cdb(5), cdb(3).
+

+
.SH LICENCE
+
Public domain.
+

added external/tinycdb/cdb.3
@@ -0,0 +1,595 @@
+
.\" $Id: cdb.3,v 1.11 2009-01-31 17:12:22 mjt Exp $
+
.\" cdb library manpage
+
.\"
+
.\" This file is a part of tinycdb package by Michael Tokarev, mjt@corpit.ru.
+
.\" Public domain.
+
.\"
+
.TH cdb 3 "Jun 2006"
+

+
.SH NAME
+
cdb \- Constant DataBase library
+

+
.SH SYNOPSYS
+

+
.nf
+
.ft B
+
 #include <cdb.h>
+
 cc ... \-lcdb
+
.ft R
+
.fi
+

+
.SH DESCRIPTION
+

+
.B cdb
+
is a library to create and access Constant DataBase files.
+
File stores (key,value) pairs and used to quickly find a
+
value based on a given key.  Cdb files are create-once files,
+
that is, once created, file cannot be updated but recreated
+
from scratch -- this is why database is called \fIconstant\fR.
+
Cdb file is optimized for quick access.  Format of such file
+
described in \fIcdb\fR(5) manpage.  This manual page corresponds
+
to version \fB0.77\fR of \fBtinycdb\fR package.
+

+
Library defines two non-interlaced interfaces: for querying
+
existing cdb file data (read-only mode) and for creating
+
such a file (almost write-only).  Strictly speaking, those
+
modes allows very limited set of opposite operation as well
+
(i.e. in query mode, it is possible to update key's value).
+

+
All routines in this library are thread-safe as no global
+
data used, except of \fIerrno\fR variable for error indication.
+

+
.B cdb
+
datafiles may be moved between systems safely, since format
+
does not depend on architecture.
+

+
.SH "QUERY MODE"
+

+
There are two query modes available.  First uses a structure
+
that represents a cdb database, just like \fBFILE\fR structure
+
in stdio library, and another works with plain filedescriptor.
+
First mode is more sophisticated and flexible, and usually somewhat
+
faster.  It uses \fBmmap\fR(2) internally.  This mode may look
+
more "natural" or object-oriented compared to second one.
+

+
The following routines works with any mode:
+

+
.nf
+
unsigned \fBcdb_unpack\fR(\fIbuf\fR)
+
   const unsigned char \fIbuf\fR[4];
+
.fi
+
.RS
+
helper routine to convert 32-bit integer from internal representation
+
to machine format.  May be used to handle application integers in
+
a portable way.  There is no error return.
+
.RE
+

+
.SS "Query Mode 1"
+

+
All query operations in first more deals with common data
+
structure, \fBstruct cdb\fR, associated with an open file
+
descriptor.  This structure is opaque to application.
+

+
The following routines exists for accessing \fBcdb\fR
+
database:
+

+
.nf
+
int \fBcdb_init\fR(\fIcdbp\fR, \fIfd\fR)
+
   struct cdb *\fIcdbp\fR;
+
   int \fIfd\fR;
+
.fi
+
.RS
+
initializes structure given by \fIcdbp\fR pointer and associates
+
it with opened file descriptor \fIfd\fR.  Memory allocation for
+
structure itself if needed and file open operation should be done
+
by application.  File \fIfd\fR should be opened at least read-only,
+
and should be seekable.  Routine returns 0 on success or negative
+
value on error.
+
.RE
+

+
.nf
+
void \fBcdb_free\fR(\fIcdbp\fR)
+
   struct cdb *\fIcdbp\fR;
+
.fi
+
.RS
+
frees internal resources held by structure.  Note that this
+
routine does \fInot\fR closes a file.
+
.RE
+

+
.nf
+
int \fBcdb_fileno\fR(\fIcdbp\fR)
+
  const struct cdb *\fIcdbp\fR;
+
.fi
+
.RS
+
returns filedescriptor associated with cdb (as was passed to
+
\fBcdb_init\fR()).
+
.RE
+

+
.nf
+
int \fBcdb_read\fR(\fIcdbp\fR, \fIbuf\fR, \fIlen\fR, \fIpos\fR)
+
int \fBcdb_readdata\fR(\fIcdbp\fR, \fIbuf\fR, \fIlen\fR, \fIpos\fR)
+
int \fBcdb_readkey\fR(\fIcdbp\fR, \fIbuf\fR, \fIlen\fR, \fIpos\fR)
+
   const struct cdb *\fIcdbp\fR;
+
   void *\fIbuf\fR;
+
   unsigned \fIlen\fR;
+
   unsigned \fIpos\fR;
+
.fi
+
.RS
+
reads a data from cdb file, starting at position \fIpos\fR of length
+
\fIlen\fR, placing result to \fIbuf\fR.  This routine may be used
+
to get actual value found by \fBcdb_find\fR() or other routines
+
that returns position and length of a data.  Returns 0 on success
+
or negative value on error.
+
Routines \fBcdb_readdata\fR() and \fBcdb_readkey\fR() are shorthands
+
to read current (after e.g. \fBcdb_find\fR()) data and key
+
respectively, using \fBcdb_read\fR().
+
.RE
+

+
.nf
+
const void *\fBcdb_get\fR(\fIcdbp\fR, \fIlen\fR, \fIpos\fR)
+
const void *\fBcdb_getdata\fR(\fIcdbp\fR)
+
const void *\fBcdb_getkey\fR(\fIcdbp\fR)
+
   const struct cdb *\fIcdbp\fR;
+
   unsigned \fIlen\fR;
+
   unsigned \fIpos\fR;
+
.fi
+
.RS
+
Internally, cdb library uses memory-mmaped region to access the on-disk
+
database.  \fBcdb_get\fR() allows to access internal memory in a way
+
similar to \fBcdb_read\fR() but without extra copying and buffer
+
allocation.  Returns pointer to actual data on success or NULL on
+
error (position points to outside of the database).
+
Routines \fBcdb_getdata\fR() and \fBcdb_getkey\fR() are shorthands
+
to access current (after e.g. \fBcdb_find\fR()) data and key
+
respectively, using \fBcdb_get\fR().
+
.RE
+

+
.nf
+
int \fBcdb_find\fR(\fIcdbp\fR, \fIkey\fR, \fIklen\fR)
+
unsigned \fBcdb_datapos\fR(\fIcdbp\fR)
+
unsigned \fBcdb_datalen\fR(\fIcdbp\fR)
+
unsigned \fBcdb_keypos\fR(\fIcdbp\fR)
+
unsigned \fBcdb_keylen\fR(\fIcdbp\fR)
+
   struct cdb *\fIcdbp\fR;
+
   const void *\fIkey\fR;
+
   unsigned \fIklen\fR;
+
.fi
+
.RS
+
attempts to find a key given by (\fIkey\fR,\fIklen\fR) parameters.
+
If key exists in database, routine returns 1 and places position
+
and length of value associated with this key to internal fields
+
inside \fIcdbp\fR structure, to be accessible by \fBcdb_datapos\fR(\fIcdbp\fR)
+
and \fBcdb_datalen\fR(\fIcdbp\fR) routines.  If key is not in database,
+
\fBcdb_find\fR() returns 0.  On error, negative value is returned.
+
Data pointers (available via \fBcdb_datapos\fR() and \fBcdb_datalen\fR())
+
gets updated only in case of successful search.  Note that using
+
\fBcdb_find\fR() it is possible to lookup only \fIfirst\fR record
+
with a given key.
+
.RE
+

+
.nf
+
int \fBcdb_findinit(\fIcdbfp\fR, \fIcdbp\fR, \fIkey\fR, \fIklen\fR)
+
int \fBcdb_findnext\fR(\fIcdbfp\fR)
+
  struct cdb_find *\fIcdbfp\fR;
+
  const struct cdb *\fIcdbp\fR;
+
  const void *\fIkey\fR;
+
  unsigned \fIklen\fR;
+
.fi
+
.RS
+
sequential-find routines that used separate structure.  It is
+
possible to have more than one record with the same key in a
+
database, and these routines allows to enumerate all them.
+
\fBcdb_findinit\fR() initializes search structure pointed to
+
by \fIcdbfp\fR.  It will return negative value on error or
+
non-negative value on success.  \fBcdb_findnext\fR() attempts
+
to find next (first when called right after \fBcdb_findinit\fR())
+
matching key, setting value position and length in \fIcdbfp\fR
+
structure.  It will return positive value if given key was
+
found, 0 if there is no more such key(s), or negative value
+
on error.  To access value position and length after successful
+
call to \fBcdb_findnext\fR() (when it returned positive result),
+
use \fBcdb_datapos\fR(\fIcdbp\fR) and \fBcdb_datalen\fR(\fIcdbp\fR)
+
routines.  It is error to continue using \fBcdb_findnext\fR() after
+
it returned 0 or error condition (\fBcdb_findinit\fR() should be
+
called again).  Current data pointers (available via \fBcdb_datapos\fR()
+
and \fBcdb_datalen\fR()) gets updated only on successful search.
+
.RE
+

+
.nf
+
void \fBcdb_seqinit\fR(\fIcptr\fR, \fIcdbp\fR)
+
int \fBcdb_seqnext\fR(\fIcptr\fR, \fIcdbp\fR)
+
  unsigned *\fIcptr\fR;
+
  struct cdb *\fIcdbp\fR;
+
.fi
+
.RS
+
sequential enumeration of all records stored in cdb file.
+
\fBcdb_seqinit\fR() initializes access current data pointer \fIcptr\fR
+
to point before first record in a cdb file. \fBcdb_seqnext\fR() updates
+
data pointers in \fIcdbp\fR to point to the next record and updates
+
\fIcptr\fR, returning positive value on success, 0 on end of data condition
+
and negative value on error.  Current record will be available after
+
successful operation using \fBcdb_datapos\fR(\fIcdbp\fR) and
+
\fBcdb_datalen\fR(\fIcdbp\fR) (for the data) and \fBcdb_keypos\fR(\fIcdbp\fR)
+
and \fBcdb_keylen\fR(\fIcdbp\fR) (for the key of the record).
+
Data pointers gets updated only in case of successful operation.
+
.RE
+

+
.SS "Query Mode 2"
+

+
In this mode, one need to open a \fBcdb\fR file using one of
+
standard system calls (such as \fBopen\fR(2)) to obtain a
+
filedescriptor, and then pass that filedescriptor to cdb routines.
+
Available methods to query a cdb database using only a filedescriptor
+
include:
+

+
.nf
+
int \fBcdb_seek\fR(\fIfd\fR, \fIkey\fR, \fIklen\fR, \fIdlenp\fR)
+
  int \fIfd\fR;
+
  const void *\fIkey\fR;
+
  unsigned \fIklen\fR;
+
  unsigned *\fIdlenp\fR;
+
.fi
+
.RS
+
searches a cdb database (as pointed to by \fIfd\fR filedescriptor)
+
for a key given by (\fIkey\fR, \fIklen\fR), and positions file pointer
+
to start of data associated with that key if found, so that next read
+
operation from this filedescriptor will read that value, and places
+
length of value, in bytes, to variable pointed to by \fIdlenp\fR.
+
Returns positive value if operation was successful, 0 if key was not
+
found, or negative value on error.  To read the data from a cdb file,
+
\fBcdb_bread\fR() routine below can be used.
+
.RE
+

+
.nf
+
int \fBcdb_bread\fR(\fIfd\fR, \fIbuf\fR, \fIlen\fR)
+
  int \fIfd\fR;
+
  void *\fIbuf\fR;
+
  int \fIlen\fR;
+
.fi
+
.RS
+
reads data from a file (as pointed to by \fIfd\fR filedescriptor) and
+
places \fIlen\fR bytes from this file to a buffer pointed to by \fIbuf\fR.
+
Returns 0 if exactly \fIlen\fR bytes was read, or a negative value in
+
case of error or end-of-file.  This routine ignores interrupt errors (EINTR).
+
Sets errno variable to \fBEIO\fR in case of end-of-file condition (when
+
there is less than \fIlen\fR bytes available to read).
+
.RE
+

+
.SS Notes
+

+
Note that \fIvalue\fR of any given key may be updated in place
+
by another value of the same size, by writing to file at position
+
found by \fBcdb_find\fR() or \fBcdb_seek\fR().  However one should
+
be very careful when doing so, since write operation may not succeed
+
in case of e.g. power failure, thus leaving corrupted data.  When
+
database is (re)created, one can guarantee that no incorrect data
+
will be written to database, but not with inplace update.  Note
+
also that it is not possible to update any key or to change length
+
of value.
+

+
.SS
+

+
.SH "CREATING MODE"
+

+
.B cdb
+
database file should usually be created in two steps: first, temporary
+
file created and written to disk, and second, that temporary file
+
is renamed to permanent place.  Unix rename(2) call is atomic operation,
+
it removes destination file if any AND renaes another file in one
+
step.  This way it is guaranteed that readers will not see incomplete
+
database.  To prevent multiple simultaneous updates, locking may
+
also be used.
+

+
All routines used to create \fBcdb\fR database works with
+
\fBstruct cdb_make\fR object that is opaque to application.
+
Application may assume that \fBstruct cdb_make\fR has at least
+
the same member(s) as published in \fBstruct cdb\fR above.
+

+
.nf
+
int \fBcdb_make_start\fR(\fIcdbmp\fR, \fIfd\fR)
+
   struct cdb_make *\fIcdbmp\fR;
+
   int \fIfd\fR;
+
.fi
+
.RS
+
initializes structure to create a database.  File \fIfd\fR should be
+
opened read-write and should be seekable.  Returns 0 on success
+
or negative value on error.
+
.RE
+

+
.nf
+
int \fBcdb_make_add\fR(\fIcdbmp\fR, \fIkey\fR, \fIklen\fR, \fIval\fR, \fIvlen\fR)
+
   struct cdb_make *\fIcdbmp\fR;
+
   const void *\fIkey\fR, *\fIval\fR;
+
   unsigned \fIklen\fR, \fIvlen\fR;
+
.fi
+
.RS
+
adds record with key (\fIkey\fR,\fIklen\fR) and value (\fIval\fR,\fIvlen\fR)
+
to a database.  Returns 0 on success or negative value on error.  Note that
+
this routine does not checks if given key already exists, but \fBcdb_find\fR()
+
will not see second record with the same key.  It is not possible to continue
+
building a database if \fBcdb_make_add\fR() returned error indicator.
+
.RE
+

+
.nf
+
int \fBcdb_make_finish\fR(\fIcdbmp\fR)
+
   struct cdb_make *\fIcdbmp\fR;
+
.fi
+
.RS
+
finalizes database file, constructing all needed indexes, and frees
+
memory structures.  It does \fInot\fR closes filedescriptor.
+
Returns 0 on success or negative value on error.
+
.RE
+

+
.nf
+
int \fBcdb_make_exists\fR(\fIcdbmp\fR, \fIkey\fR, \fIklen\fR)
+
   struct cdb_make *\fIcdbmp\fR;
+
   const void *\fIkey\fR;
+
   unsigned \fIklen\fR;
+
.fi
+
.RS
+
This routine attempts to find given by (\fIkey\fR,\fIklen\fR) key in
+
a not-yet-complete database.  It may significantly slow down the
+
whole process, and currently it flushes internal buffer to disk on
+
every call with key those hash value already exists in db.  Returns
+
0 if such key doesn't exists, 1 if it is, or negative value on error.
+
Note that database file should be opened read-write (not write-only)
+
to use this routine.  If \fBcdb_make_exists\fR() returned error, it
+
may be not possible to continue constructing database.
+
.RE
+

+
.nf
+
int \fBcdb_make_find\fR(\fIcdbmp\fR, \fIkey\fR, \fIklen\fR, \fImode\fR)
+
   struct cdb_make *\fIcdbmp\fR;
+
   const void *\fIkey\fR;
+
   unsigned \fIklen\fR;
+
   int \fImode\fR;
+
.fi
+
.RS
+
This routine attempts to find given by (\fIkey\fR,\fIklen\fR) key in
+
the database being created.  If the given key is already exists, it
+
an action specified by \fImode\fR will be performed:
+
.IP \fBCDB_FIND\fR
+
checks whenever the given record is already in the database.
+
.IP \fBCDB_FIND_REMOVE\fR
+
removes all matching records by re-writing the database file accordingly.
+
.IP \fBCDB_FIND_FILL0\fR
+
fills all matching records with zeros and removes them from index so that
+
the records in question will not be findable with \fBcdb_find\fR().  This
+
is faster than CDB_FIND_REMOVE, but leaves zero "gaps" in the database.
+
Lastly inserted records, if matched, are always removed.
+
.PP
+
If no matching keys was found, routine returns 0.  In case at least one
+
record has been found/removed, positive value will be returned.  On
+
error, negative value will be returned and \fBerrno\fR will be set
+
appropriately.  When \fBcdb_make_find\fR() returned negative value in
+
case of error, it is not possible to continue constructing the database.
+
.PP
+
\fBcdb_make_exists\fR() is the same as calling \fBcdb_make_find\fR() with
+
\fImode\fR set to CDB_FIND.
+
.RE
+

+
.nf
+
int \fBcdb_make_put\fR(\fIcdbmp\fR, \fIkey\fR, \fIklen\fR, \fIval\fR, \fIvlen\fR, \fImode\fR)
+
   struct cdb_make *\fIcdbmp\fR;
+
   const void *\fIkey\fR, *\fIval\fR;
+
   unsigned \fIklen\fR, \fIvlen\fR;
+
   int \fImode\fR;
+
.fi
+
.RS
+
This is a somewhat combined \fBcdb_make_exists\fR() and
+
\fBcdb_make_add\fR() routines.  \fImode\fR argument controls how
+
repeated (already existing) keys will be treated:
+
.IP \fBCDB_PUT_ADD\fR
+
no duplicate checking will be performed.  This mode is the same as
+
\fBcdb_make_add\fR() routine does.
+
.IP \fBCDB_PUT_REPLACE\fR
+
If the key already exists, it will be removed from the database
+
before adding new key,value pair.  This requires moving data in
+
the file, and can be quite slow if the file is large.
+
All matching old records will be removed this way.  This is the
+
same as calling \fBcdb_make_find\fR() with CDB_FIND_REMOVE
+
\fImode\fR argument followed by calling \fBcdb_make_add\fR().
+
.IP \fBCDB_PUT_REPLACE0\fR
+
If the key already exists and it isn't the last record in the file,
+
old record will be zeroed out before adding new key,value pair.
+
This is alot faster than CDB_PUT_REPLACE, but some extra data will
+
still be present in the file.  The data -- old record -- will not
+
be accessible by normal searches, but will appear in sequential
+
database traversal.  This is the same as calling \fBcdb_make_find\fR()
+
with CDB_FIND_FILL0 \fImode\fR argument followed by \fBcdb_make_add\fR().
+
.IP \fBCDB_PUT_INSERT\fR
+
add key,value pair only if such key does not exists in a database.
+
Note that since query (see query mode above) will find first added
+
record, this mode is somewhat useless (but allows to reduce database
+
size in case of repeated keys).  This is the same as calling
+
\fBcdb_make_exists\fR(), followed by \fBcdb_make_add\fR() if
+
the key was not found.
+
.IP \fBCDB_PUT_WARN\fR
+
add key,value pair unconditionally, but also check if this key
+
already exists.  This is equivalent of \fBcdb_make_exists\fR()
+
to check existence of the given key, unconditionally followed
+
by \fBcdb_make_add\fR().
+
.PP
+
If any error occurred during operations, the routine will return
+
negative integer and will set global variable \fBerrno\fR to
+
indicate reason of failure.  In case of successful operation
+
and no duplicates found, routine will return 0.  If any duplicates
+
has been found or removed (which, in case of CDB_PUT_INSERT mode,
+
indicates that the new record was not added), routine will return
+
positive value.  If an error occurred and \fBcdb_make_put\fR() returned
+
negative error, it is not possible to continue database construction
+
process.
+
.PP
+
As with \fBcdb_make_exists\fR() and \fBcdb_make_find\fR(), usage
+
of this routine with any but CDB_PUT_ADD mode can significantly
+
slow down database creation process, especially when \fImode\fR
+
is equal to CDB_PUT_REPLACE0.
+

+
.RE
+
.nf
+
void \fBcdb_pack\fR(\fInum\fR, \fIbuf\fR)
+
   unsigned \fInum\fR;
+
   unsigned char \fIbuf\fR[4];
+
.fi
+
.RS
+
helper routine that used internally to convert machine integer \fIn\fR
+
to internal form to be stored in datafile.  32-bit integer is stored in
+
4 bytes in network byte order.  May be used to handle application data.
+
There is no error return.
+
.RE
+

+
.nf
+
unsigned \fBcdb_hash\fR(\fIbuf\fR, \fIlen\fR)
+
   const void *\fIbuf\fR;
+
   unsigned \fIlen\fR;
+
.fi
+
.RS
+
helper routine that calculates cdb hash value of given bytes.
+
CDB hash function is
+
.br
+
  hash[n] = (hash[n\-1] + (hash[n\-1] << 5)) ^ buf[n]
+
.br
+
starting with
+
.br
+
  hash[\-1] = 5381
+
.br
+
.RE
+

+
.SH ERRORS
+

+
.B cdb
+
library may set \fBerrno\fR to following on error:
+

+
.IP EPROTO
+
database file is corrupted in some way
+
.IP EINVAL
+
the same as EPROTO above if system lacks EPROTO constant
+
.IP EINVAL
+
\fIflag\fR argument for \fBcdb_make_put\fR() is invalid
+
.IP EEXIST
+
\fIflag\fR argument for \fBcdb_make_put\fR() is CDB_PUT_INSERT,
+
and key already exists
+
.IP ENOMEM
+
not enough memory to complete operation (\fBcdb_make_finish\fR and
+
\fBcdb_make_add\fR)
+
.IP EIO
+
set by \fBcdb_bread\fR and \fBcdb_seek\fR if a cdb file is shorter
+
than expected or corrupted in some other way.
+

+
.SH EXAMPLES
+

+
.PP
+
Note: in all examples below, error checking is not shown for brewity.
+

+
.SS "Query Mode"
+

+
.nf
+
 int fd;
+
 struct cdb cdb;
+
 char *key, *data;
+
 unsigned keylen, datalen;
+

+
 /* opening the database */
+
 fd = open(filename, O_RDONLY);
+
 cdb_init(&cdb, fd);
+
 /* initialize key and keylen here */
+

+
 /* single\-record search. */
+
 if (cdb_find(&cdb, key, keylen) > 0) {
+
   datalen = cdb_datalen(&cdb);
+
   data = malloc(datalen + 1);
+
   cdb_read(&cdb, data, datalen, cdb_datapos(&cdb));
+
   data[datalen] = '\\0';
+
   printf("key=%s data=%s\\n", key, data);
+
   free(data);
+
 }
+
 else
+
   printf("key=%s not found\\n", key);
+

+
 /* multiple record search */
+
 struct cdb_find cdbf;
+
 int n;
+
 cdb_findinit(&cdbf, &cdb, key, keylen);
+
 n = 0;
+
 while(cdb_findnext(&cdbf) > 0) {
+
   datalen = cdb_datalen(&cdb);
+
   data = malloc(datalen + 1);
+
   cdb_read(&cdb, data, datalen, cdb_datapos(&cdb));
+
   data[datalen] = '\\0';
+
   printf("key=%s data=%s\\n", key, data);
+
   free(data);
+
   ++n;
+
 }
+
 printf("key=%s %d records found\\n", n);
+

+
 /* sequential database access */
+
 unsigned pos;
+
 int n;
+
 cdb_seqinit(&cdb, &cpos);
+
 n = 0;
+
 while(cdb_seqnext(&cdb, &cpos) > 0) {
+
   keylen = cdb_keylen(&cdb);
+
   key = malloc(keylen + 1);
+
   cdb_read(&cdb, key, keylen, cdb_keypos(&cdb));
+
   key[keylen] = '\\0';
+
   datalen = cdb_datalen(&cdb);
+
   data = malloc(datalen + 1);
+
   cdb_read(&cdb, data, datalen, cdb_datapos(&cdb));
+
   data[datalen] = '\\0';
+
   ++n;
+
   printf("record %n: key=%s data=%s\\n", n, key, data);
+
   free(data); free(key);
+
 }
+
 printf("total records found: %d\\n", n);
+

+
 /* close the database */
+
 cdb_free(&cdb);
+
 close(fd);
+

+
 /* simplistic query mode */
+
 fd = open(filename, O_RDONLY);
+
 if (cdb_seek(fd, key, keylen, &datalen) > 0) {
+
   data = malloc(datalen + 1);
+
   cdb_bread(fd, data, datalen);
+
   data[datalen] = '\\0';
+
   printf("key=%s data=%s\\n", key, data);
+
 }
+
 else
+
   printf("key=%s not found\\n", key);
+
 close(fd);
+
.fi
+

+
.SS "Create Mode"
+

+
.nf
+
 int fd;
+
 struct cdb_make cdbm;
+
 char *key, *data;
+
 unsigned keylen, datalen;
+

+
 /* initialize the database */
+
 fd = open(filename, O_RDWR|O_CREAT|O_TRUNC, 0644);
+
 cdb_make_start(&cdbm, fd);
+

+
 while(have_more_data()) {
+
   /* initialize key and data */
+
   if (cdb_make_exists(&cdbm, key, keylen) == 0)
+
     cdb_make_add(&cdbm, key, keylen, data, datalen);
+
   /* or use cdb_make_put() with appropriate flags */
+
 }
+

+
 /* finalize and close the database */
+
 cdb_make_finish(&cdbm);
+
 close(fd);
+
.fi
+

+
.SH "SEE ALSO"
+
cdb(5), cdb(1), dbm(3), db(3), open(2).
+

+
.SH AUTHOR
+
The \fBtinycdb\fR package written by Michael Tokarev <mjt@corpit.ru>,
+
based on ideas and shares file format with original cdb library by
+
Dan Bernstein.
+

+
.SH LICENSE
+
Public domain.
added external/tinycdb/cdb.5
@@ -0,0 +1,79 @@
+
.\" $Id: cdb.5,v 1.1 2005-04-10 22:17:14 mjt Exp $
+
.\" cdb file format manpage
+
.\"
+
.\" This file is a part of tinycdb package by Michael Tokarev, mjt@corpit.ru.
+
.\" Public domain.
+
.\"
+
.TH cdb 5 "Apr, 2005"
+

+
.SH NAME
+
cdb \- Constant DataBase file format
+

+
.SH DESCRIPTION
+

+
A \fBcdb\fR database is a single file used to map `keys'
+
to `values', having records of (key,value) pairs.  File
+
consists of 3 parts: toc (table of contents), data and
+
index (hash tables).
+

+
Toc has fixed length of 2048 bytes, containing 256 pointers
+
to hash tables inside index sections.  Every pointer consists
+
of position of a hash table in bytes from the beginning of
+
a file, and a size of a hash table in entries, both are
+
4-bytes (32 bits) unsigned integers in little-endian form.
+
Hash table length may have zero length, meaning that
+
corresponding hash table is empty.
+

+
Right after toc section, data section follows without any
+
alingment.  It consists of series of records, each is a
+
key length, value (data) length, key and value.  Again,
+
key and value length are 4-byte unsigned integers.  Each
+
next record follows previous without any special alignment.
+

+
After data section, index (hash tables) section follows.
+
It should be looked to in conjunction with toc section,
+
where each of max 256 hash tables are defined.  Index
+
section consists of series of hash tables, with starting
+
position and length defined in toc section.  Every hash
+
table is a sequence of records each holds two numbers:
+
key's hash value and record position inside data section
+
(bytes from the beginning of a file to first byte of
+
key length starting data record).  If record position
+
is zero, then this is an empty hash table slot, pointed
+
to nowhere.
+

+
CDB hash function is
+
.nf
+
  hv = ((hv << 5) + hv) ^ \fIc\fR
+
.fi
+
for every single \fIc\fR byte of a key, starting with
+
hv = \fI5381\fR.
+

+
Toc section indexed by (hv % 256), i.e. hash value modulo
+
256 (number of entries in toc section).
+

+
In order to find a record, one should: first, compute the hash
+
value (hv) of a key.  Second, look to hash table number hv modulo
+
256.  If it is empty, then there is no such key exists.  If it
+
is not empty, then third, loop by slots inside that hash table,
+
starting from slot with number hv divided by 256 modulo length
+
of that table, or ((hv / 256) % htlen), searching for this hv
+
in hash table.  Stop search on empty slot (if record position
+
is zero) or when all slots was probed (note cyclic search,
+
jumping from end to beginning of a table).  When hash value in
+
question is found in hash table, look to key of corresponding
+
record, comparing it with key in question.  If them of the same
+
length and equals to each other, then record is found, overwise,
+
repeat with next hash table slot.  Note that there may be several
+
records with the same key.
+

+
.SH SEE ALSO
+
cdb(1), cdb(3).
+

+
.SH AUTHOR
+
The \fBtinycdb\fR package written by Michael Tokarev <mjt@corpit.ru>,
+
based on ideas and shares file format with original cdb library by
+
Dan Bernstein.
+

+
.SH LICENSE
+
Public domain.
added external/tinycdb/cdb.c
@@ -0,0 +1,540 @@
+
/* $Id: cdb.c,v 1.18 2008-11-06 22:37:33 mjt Exp $
+
 * cdb command line tool
+
 *
+
 * This file is a part of tinycdb package by Michael Tokarev, mjt@corpit.ru.
+
 * Public domain.
+
 */
+

+
#define _GNU_SOURCE	/* #define this even on Windows */
+

+
#ifdef _WIN32		/* by the way, how about win64? */
+
# include <io.h>
+
# include <malloc.h>
+
# pragma warning(disable: 4996)
+
#else
+
# include <unistd.h>
+
#endif
+

+
#include <stdio.h>
+
#include <stdlib.h>
+
#include <string.h>
+
#include <stdarg.h>
+
#include <fcntl.h>
+
#include <errno.h>
+
#include "cdb.h"
+

+
#ifndef EPROTO
+
# define EPROTO EINVAL
+
#endif
+

+
#ifdef __GLIBC__
+
# define HAVE_PROGRAM_INVOCATION_SHORT_NAME
+
#endif
+

+
#ifdef HAVE_PROGRAM_INVOCATION_SHORT_NAME
+
# define progname program_invocation_short_name
+
#else
+
static char *progname;
+
#endif
+

+
#ifndef O_NOFOLLOW
+
# define O_NOFOLLOW 0
+
#endif
+

+
#ifdef _WIN32
+
# define FBINMODE "b"
+
#else
+
# define FBINMODE
+
#endif
+

+
#define F_DUPMASK	0x000f
+
#define F_WARNDUP	0x0100
+
#define F_ERRDUP	0x0200
+
#define F_MAP		0x1000	/* map format (or else CDB native format) */
+

+
/* Silly defines just to suppress silly compiler warnings.
+
 * The thing is, trivial routines like strlen(), fgets() etc expects
+
 * char* argument, and GCC>=4 complains about using unsigned char* here.
+
 * Silly silly silly.
+
 */
+
#ifdef __GNUC__
+
static inline size_t ustrlen(const unsigned char *s) {
+
  return strlen((const char*)s);
+
}
+
static inline unsigned char *ufgets(unsigned char *s, int size, FILE *f) {
+
  return (unsigned char*)fgets((char*)s, size, f);
+
}
+
#else
+
# define ustrlen strlen
+
# define ufgets fgets
+
#endif
+

+
static unsigned char *buf;
+
static unsigned blen;
+

+
static void
+
#ifdef __GNUC__
+
__attribute__((noreturn,format(printf,2,3)))
+
#endif
+
error(int errnum, const char *fmt, ...)
+
{
+
  if (fmt) {
+
    va_list ap;
+
    fprintf(stderr, "%s: ", progname);
+
    va_start(ap, fmt);
+
    vfprintf(stderr, fmt, ap);
+
    va_end(ap);
+
  }
+
  if (errnum)
+
    fprintf(stderr, ": %s\n", strerror(errnum));
+
  else {
+
    if (fmt) putc('\n', stderr);
+
    fprintf(stderr, "%s: try `%s -h' for help\n", progname, progname);
+
  }
+
  fflush(stderr);
+
  exit(errnum ? 111 : 2);
+
}
+

+
static void allocbuf(unsigned len) {
+
  if (blen < len) {
+
    buf = (unsigned char*)(buf ? realloc(buf, len) : malloc(len));
+
    if (!buf)
+
      error(ENOMEM, "unable to allocate %u bytes", len);
+
    blen = len;
+
  }
+
}
+

+
static int qmode(char *dbname, const char *key, int num, int flags)
+
{
+
  struct cdb c;
+
  struct cdb_find cf;
+
  int r;
+
  int n, found;
+

+
  r = open(dbname, O_RDONLY);
+
  if (r < 0 || cdb_init(&c, r) != 0)
+
    error(errno, "unable to open database `%s'", dbname);
+

+
  r = cdb_findinit(&cf, &c, key, strlen(key));
+
  if (!r)
+
    return 100;
+
  else if (r < 0)
+
    error(errno, "%s", key);
+
  n = 0; found = 0;
+
  while((r = cdb_findnext(&cf)) > 0) {
+
    ++n;
+
    if (num && num != n) continue;
+
    ++found;
+
    allocbuf(cdb_datalen(&c));
+
    if (cdb_read(&c, buf, cdb_datalen(&c), cdb_datapos(&c)) != 0)
+
      error(errno, "unable to read value");
+
    fwrite(buf, 1, cdb_datalen(&c), stdout);
+
    if (flags & F_MAP) putchar('\n');
+
    if (num)
+
      break;
+
  }
+
  if (r < 0)
+
    error(0, "%s", key);
+
  return found ? 0 : 100;
+
}
+

+
static void
+
fget(FILE *f, unsigned char *b, unsigned len, unsigned *posp, unsigned limit)
+
{
+
  if (posp && limit - *posp < len)
+
    error(EPROTO, "invalid database format");
+
  if (fread(b, 1, len, f) != len) {
+
    if (ferror(f)) error(errno, "unable to read");
+
    fprintf(stderr, "%s: unable to read: short file\n", progname);
+
    exit(2);
+
  }
+
  if (posp) *posp += len;
+
}
+

+
static int
+
fcpy(FILE *fi, FILE *fo, unsigned len, unsigned *posp, unsigned limit)
+
{
+
  while(len > blen) {
+
    fget(fi, buf, blen, posp, limit);
+
    if (fo && fwrite(buf, 1, blen, fo) != blen) return -1;
+
    len -= blen;
+
  }
+
  if (len) {
+
    fget(fi, buf, len, posp, limit);
+
    if (fo && fwrite(buf, 1, len, fo) != len) return -1;
+
  }
+
  return 0;
+
}
+

+
static int
+
dmode(char *dbname, char mode, int flags)
+
{
+
  unsigned eod, klen, vlen;
+
  unsigned pos = 0;
+
  FILE *f;
+
  if (strcmp(dbname, "-") == 0)
+
    f = stdin;
+
  else if ((f = fopen(dbname, "r" FBINMODE)) == NULL)
+
    error(errno, "open %s", dbname);
+
  allocbuf(2048);
+
  fget(f, buf, 2048, &pos, 2048);
+
  eod = cdb_unpack(buf);
+
  while(pos < eod) {
+
    fget(f, buf, 8, &pos, eod);
+
    klen = cdb_unpack(buf);
+
    vlen = cdb_unpack(buf + 4);
+
    if (!(flags & F_MAP))
+
      if (printf(mode == 'd' ? "+%u,%u:" : "+%u:", klen, vlen) < 0) return -1;
+
    if (fcpy(f, stdout, klen, &pos, eod) != 0) return -1;
+
    if (mode == 'd')
+
      if (fputs(flags & F_MAP ? " " : "->", stdout) < 0)
+
        return -1;
+
    if (fcpy(f, mode == 'd' ? stdout : NULL, vlen, &pos, eod) != 0)
+
      return -1;
+
    if (putc('\n', stdout) < 0)
+
      return -1;
+
  }
+
  if (pos != eod)
+
    error(EPROTO, "invalid cdb file format");
+
  if (!(flags & F_MAP))
+
    if (putc('\n', stdout) < 0)
+
      return -1;
+
  return 0;
+
}
+

+
static int smode(char *dbname) {
+
  FILE *f;
+
  unsigned pos, eod;
+
  unsigned cnt = 0;
+
  unsigned kmin = 0, kmax = 0, ktot = 0;
+
  unsigned vmin = 0, vmax = 0, vtot = 0;
+
  unsigned hmin = 0, hmax = 0, htot = 0, hcnt = 0;
+
#define NDIST 11
+
  unsigned dist[NDIST];
+
  unsigned char toc[2048];
+
  unsigned k;
+

+
  if (strcmp(dbname, "-") == 0)
+
    f = stdin;
+
  else if ((f = fopen(dbname, "r" FBINMODE)) == NULL)
+
    error(errno, "open %s", dbname);
+

+
  pos = 0;
+
  fget(f, toc, 2048, &pos, 2048);
+

+
  allocbuf(2048);
+

+
  eod = cdb_unpack(toc);
+
  while(pos < eod) {
+
    unsigned klen, vlen;
+
    fget(f, buf, 8, &pos, eod);
+
    klen = cdb_unpack(buf);
+
    vlen = cdb_unpack(buf + 4);
+
    fcpy(f, NULL, klen, &pos, eod);
+
    fcpy(f, NULL, vlen, &pos, eod);
+
    ++cnt;
+
    ktot += klen;
+
    if (!kmin || kmin > klen) kmin = klen;
+
    if (kmax < klen) kmax = klen;
+
    vtot += vlen;
+
    if (!vmin || vmin > vlen) vmin = vlen;
+
    if (vmax < vlen) vmax = vlen;
+
    vlen += klen;
+
  }
+
  if (pos != eod) error(EPROTO, "invalid cdb file format");
+

+
  for (k = 0; k < NDIST; ++k)
+
    dist[k] = 0;
+
  for (k = 0; k < 256; ++k) {
+
    unsigned i = cdb_unpack(toc + (k << 3));
+
    unsigned hlen = cdb_unpack(toc + (k << 3) + 4);
+
    if (i != pos) error(EPROTO, "invalid cdb hash table");
+
    if (!hlen) continue;
+
    for (i = 0; i < hlen; ++i) {
+
      unsigned h;
+
      fget(f, buf, 8, &pos, 0xffffffff);
+
      if (!cdb_unpack(buf + 4)) continue;
+
      h = (cdb_unpack(buf) >> 8) % hlen;
+
      if (h == i) h = 0;
+
      else {
+
        if (h < i) h = i - h;
+
        else h = hlen - h + i;
+
        if (h >= NDIST) h = NDIST - 1;
+
      }
+
      ++dist[h];
+
    }
+
    if (!hmin || hmin > hlen) hmin = hlen;
+
    if (hmax < hlen) hmax = hlen;
+
    htot += hlen;
+
    ++hcnt;
+
  }
+
  printf("number of records: %u\n", cnt);
+
  printf("key min/avg/max length: %u/%u/%u\n",
+
         kmin, cnt ? (ktot + cnt / 2) / cnt : 0, kmax);
+
  printf("val min/avg/max length: %u/%u/%u\n",
+
         vmin, cnt ? (vtot + cnt / 2) / cnt : 0, vmax);
+
  printf("hash tables/entries/collisions: %u/%u/%u\n",
+
         hcnt, htot, cnt - dist[0]);
+
  printf("hash table min/avg/max length: %u/%u/%u\n",
+
         hmin, hcnt ? (htot + hcnt / 2) / hcnt : 0, hmax);
+
  printf("hash table distances:\n");
+
  for(k = 0; k < NDIST; ++k)
+
    printf(" %c%u: %6u %2u%%\n",
+
           k == NDIST - 1 ? '>' : 'd', k == NDIST - 1 ? k - 1 : k,
+
           dist[k], cnt ? dist[k] * 100 / cnt : 0);
+
  return 0;
+
}
+

+
static void badinput(const char *fn) {
+
  fprintf(stderr, "%s: %s: bad format\n", progname, fn);
+
  exit(2);
+
}
+

+
static int getnum(FILE *f, unsigned *np, const char *fn) {
+
  unsigned n;
+
  int c = getc(f);
+
  if (c < '0' || c > '9') badinput(fn);
+
  n = c - '0';
+
  while((c = getc(f)) >= '0' && c <= '9') {
+
    c -= '0';
+
    if (0xffffffff / 10 - c < n) badinput(fn);
+
    n = n * 10 + c;
+
  }
+
  *np = n;
+
  return c;
+
}
+

+
static void
+
addrec(struct cdb_make *cdbmp,
+
       const unsigned char *key, unsigned klen,
+
       const unsigned char *val, unsigned vlen,
+
       int flags)
+
{
+
  int r = cdb_make_put(cdbmp, key, klen, val, vlen, flags & F_DUPMASK);
+
  if (r < 0)
+
    error(errno, "cdb_make_put");
+
  else if (r && (flags & F_WARNDUP)) {
+
    fprintf(stderr, "%s: key `", progname);
+
    fwrite(key, 1, klen, stderr);
+
    fputs("' duplicated\n", stderr);
+
    if (flags & F_ERRDUP)
+
      exit(1);
+
  }
+
}
+

+
static void
+
dofile_cdb(struct cdb_make *cdbmp, FILE *f, const char *fn, int flags)
+
{
+
  unsigned klen, vlen;
+
  int c;
+
  while((c = getc(f)) == '+') {
+
    if ((c = getnum(f, &klen, fn)) != ',' ||
+
        (c = getnum(f, &vlen, fn)) != ':' ||
+
        0xffffffff - klen < vlen)
+
      badinput(fn);
+
    allocbuf(klen + vlen);
+
    fget(f, buf, klen, NULL, 0);
+
    if (getc(f) != '-' || getc(f) != '>') badinput(fn);
+
    fget(f, buf + klen, vlen, NULL, 0);
+
    if (getc(f) != '\n') badinput(fn);
+
    addrec(cdbmp, buf, klen, buf + klen, vlen, flags);
+
  }
+
  if (c != '\n') badinput(fn);
+
}
+

+
static void
+
dofile_ln(struct cdb_make *cdbmp, FILE *f, int flags)
+
{
+
  unsigned char *k, *v;
+
  while(ufgets(buf, blen, f) != NULL) {
+
    unsigned l = 0;
+
    for (;;) {
+
      l += ustrlen(buf + l);
+
      v = buf + l;
+
      if (v > buf && v[-1] == '\n') {
+
        v[-1] = '\0';
+
        break;
+
      }
+
      if (l < blen)
+
        allocbuf(l + 512);
+
      if (!ufgets(buf + l, blen - l, f))
+
        break;
+
    }
+
    k = buf;
+
    while(*k == ' ' || *k == '\t') ++k;
+
    if (!*k || *k == '#')
+
      continue;
+
    v = k;
+
    while(*v && *v != ' ' && *v != '\t') ++v;
+
    if (*v) *v++ = '\0';
+
    while(*v == ' ' || *v == '\t') ++v;
+
    addrec(cdbmp, k, ustrlen(k), v, ustrlen(v), flags);
+
  }
+
}
+

+
static void
+
dofile(struct cdb_make *cdbmp, FILE *f, const char *fn, int flags)
+
{
+
  if (flags & F_MAP)
+
    dofile_ln(cdbmp, f, flags);
+
  else
+
    dofile_cdb(cdbmp, f, fn, flags);
+
  if (ferror(f))
+
    error(errno, "read error");
+
}
+

+
static int
+
cmode(char *dbname, char *tmpname, int argc, char **argv, int flags, int perms)
+
{
+
  struct cdb_make cdb;
+
  int fd;
+
  if (!tmpname) {
+
    tmpname = (char*)malloc(strlen(dbname) + 5);
+
    if (!tmpname)
+
      error(ENOMEM, "unable to allocate memory");
+
    /* OpenBSD compiler complains about strcat() and strcpy() usage,
+
     * and suggests to replace them with (non-standard) strlcat() and
+
     * strlcpy().  This is silly, since it's obvious that usage of
+
     * original str*() routines here is correct.
+
     * This is compiler/environment bug, not tinycdb bug, so please
+
     * fix it in proper place, and don't send patches to me.  Thank you.
+
     */
+
    strcat(strcpy(tmpname, dbname), ".tmp");
+
  }
+
  else if (strcmp(tmpname, "-") == 0 || strcmp(tmpname, dbname) == 0)
+
    tmpname = dbname;
+
  if (perms >= 0)
+
    umask(0);
+
  unlink(tmpname);
+
  fd = open(tmpname, O_RDWR|O_CREAT|O_EXCL|O_NOFOLLOW,
+
            perms >= 0 ? perms : 0666);
+
  if (fd < 0)
+
    error(errno, "unable to create %s", tmpname);
+
  cdb_make_start(&cdb, fd);
+
  allocbuf(4096);
+
  if (argc) {
+
    int i;
+
    for (i = 0; i < argc; ++i) {
+
      if (strcmp(argv[i], "-") == 0)
+
        dofile(&cdb, stdin, "(stdin)", flags);
+
      else {
+
        FILE *f = fopen(argv[i], "r");
+
        if (!f)
+
          error(errno, "%s", argv[i]);
+
        dofile(&cdb, f, argv[i], flags);
+
        fclose(f);
+
      }
+
    }
+
  }
+
  else
+
    dofile(&cdb, stdin, "(stdin)", flags);
+
  if (cdb_make_finish(&cdb) != 0)
+
    error(errno, "cdb_make_finish");
+
  close(fd);
+
  if (tmpname != dbname)
+
    if (rename(tmpname, dbname) != 0)
+
      error(errno, "rename %s->%s", tmpname, dbname);
+
  return 0;
+
}
+

+
int main(int argc, char **argv)
+
{
+
  int c;
+
  char mode = 0;
+
  char *tmpname = NULL;
+
  int flags = 0;
+
  int num = 0;
+
  int r;
+
  int perms = -1;
+
  extern char *optarg;
+
  extern int optind;
+

+
#ifdef HAVE_PROGRAM_INVOCATION_SHORT_NAME
+
  argv[0] = progname;
+
#else
+
  if (argv[0] && (progname = strrchr(argv[0], '/')) != NULL)
+
    argv[0] = ++progname;
+
  else
+
    progname = argv[0];
+
#endif
+

+
  if (argc <= 1)
+
    error(0, "no arguments given");
+

+
  while((c = getopt(argc, argv, "qdlcsht:n:mwruep:0")) != EOF)
+
    switch(c) {
+
    case 'q': case 'd':  case 'l': case 'c': case 's':
+
      if (mode && mode != c)
+
        error(0, "different modes of operation requested");
+
      mode = c;
+
      break;
+
    case 't': tmpname = optarg; break;
+
    case 'w': flags |= F_WARNDUP; break;
+
    case 'e': flags |= F_WARNDUP | F_ERRDUP; break;
+
    case 'r': flags = (flags & ~F_DUPMASK) | CDB_PUT_REPLACE; break;
+
    case 'u': flags = (flags & ~F_DUPMASK) | CDB_PUT_INSERT; break;
+
    case '0': flags = (flags & ~F_DUPMASK) | CDB_PUT_REPLACE0; break;
+
    case 'm': flags |= F_MAP; break;
+
    case 'p': {
+
      char *ep = NULL;
+
      perms = strtol(optarg, &ep, 0);
+
      if (perms < 0 || perms > 0777 || (ep && *ep))
+
        error(0, "invalid permissions `%s'", optarg);
+
      break;
+
    }
+
    case 'n': {
+
      char *ep = NULL;
+
      if ((num = strtol(optarg, &ep, 0)) <= 0 || (ep && *ep))
+
        error(0, "invalid record number `%s'", optarg);
+
      break;
+
    }
+
    case 'h':
+
#define strify(x) _strify(x)
+
#define _strify(x) #x
+
      printf("\
+
%s: Constant DataBase (CDB) tool version " strify(TINYCDB_VERSION)
+
". Usage is:\n\
+
 query:  %s -q [-m] [-n recno|-a] cdbfile key\n\
+
 dump:   %s -d [-m] [cdbfile|-]\n\
+
 list:   %s -l [-m] [cdbfile|-]\n\
+
 create: %s -c [-m] [-wrue0] [-t tempfile|-] [-p perms] cdbfile [infile...]\n\
+
 stats:  %s -s [cdbfile|-]\n\
+
 help:   %s -h\n\
+
", progname, progname, progname, progname, progname, progname, progname);
+
      return 0;
+

+
    default:
+
      error(0, NULL);
+
    }
+

+
  argv += optind;
+
  argc -= optind;
+
  switch(mode) {
+
    case 'q':
+
      if (argc < 2) error(0, "no database or key to query specified");
+
      if (argc > 2) error(0, "extra arguments in command line");
+
      r = qmode(argv[0], argv[1], num, flags);
+
      break;
+
    case 'c':
+
      if (!argc) error(0, "no database name specified");
+
      if ((flags & F_WARNDUP) && !(flags & F_DUPMASK))
+
        flags |= CDB_PUT_WARN;
+
      r = cmode(argv[0], tmpname, argc - 1, argv + 1, flags, perms);
+
      break;
+
    case 'd':
+
    case 'l':
+
      if (argc > 1) error(0, "extra arguments for dump/list");
+
      r = dmode(argc ? argv[0] : "-", mode, flags);
+
      break;
+
    case 's':
+
      if (argc > 1) error(0, "extra argument(s) for stats");
+
      r = smode(argc ? argv[0] : "-");
+
      break;
+
    default:
+
      error(0, "no -q, -c, -d, -l or -s option specified");
+
  }
+
  if (r < 0 || fflush(stdout) < 0)
+
    error(errno, "unable to write: %d", c);
+
  return r;
+
}
+

added external/tinycdb/cdb.h
@@ -0,0 +1,126 @@
+
/* $Id: cdb.h,v 1.10 2009-01-31 17:12:22 mjt Exp $
+
 * public cdb include file
+
 *
+
 * This file is a part of tinycdb package by Michael Tokarev, mjt@corpit.ru.
+
 * Public domain.
+
 */
+

+
#ifndef TINYCDB_VERSION
+
#define TINYCDB_VERSION 0.77
+

+
#ifdef __cplusplus
+
extern "C" {
+
#endif
+

+
typedef unsigned int cdbi_t; /* compatibility */
+

+
/* common routines */
+
unsigned cdb_hash(const void *buf, unsigned len);
+
unsigned cdb_unpack(const unsigned char buf[4]);
+
void cdb_pack(unsigned num, unsigned char buf[4]);
+

+
struct cdb {
+
  int cdb_fd;			/* file descriptor */
+
  /* private members */
+
  unsigned cdb_fsize;		/* datafile size */
+
  unsigned cdb_dend;		/* end of data ptr */
+
  const unsigned char *cdb_mem; /* mmap'ed file memory */
+
  unsigned cdb_vpos, cdb_vlen;	/* found data */
+
  unsigned cdb_kpos, cdb_klen;	/* found key */
+
};
+

+
#define CDB_STATIC_INIT {0,0,0,0,0,0,0,0}
+

+
#define cdb_datapos(c) ((c)->cdb_vpos)
+
#define cdb_datalen(c) ((c)->cdb_vlen)
+
#define cdb_keypos(c) ((c)->cdb_kpos)
+
#define cdb_keylen(c) ((c)->cdb_klen)
+
#define cdb_fileno(c) ((c)->cdb_fd)
+

+
int cdb_init(struct cdb *cdbp, int fd);
+
void cdb_free(struct cdb *cdbp);
+

+
int cdb_read(const struct cdb *cdbp,
+
             void *buf, unsigned len, unsigned pos);
+
#define cdb_readdata(cdbp, buf) \
+
        cdb_read((cdbp), (buf), cdb_datalen(cdbp), cdb_datapos(cdbp))
+
#define cdb_readkey(cdbp, buf) \
+
        cdb_read((cdbp), (buf), cdb_keylen(cdbp), cdb_keypos(cdbp))
+

+
const void *cdb_get(const struct cdb *cdbp, unsigned len, unsigned pos);
+
#define cdb_getdata(cdbp) \
+
        cdb_get((cdbp), cdb_datalen(cdbp), cdb_datapos(cdbp))
+
#define cdb_getkey(cdbp) \
+
        cdb_get((cdbp), cdb_keylen(cdbp), cdb_keypos(cdbp))
+

+
int cdb_find(struct cdb *cdbp, const void *key, unsigned klen);
+

+
struct cdb_find {
+
  struct cdb *cdb_cdbp;
+
  unsigned cdb_hval;
+
  const unsigned char *cdb_htp, *cdb_htab, *cdb_htend;
+
  unsigned cdb_httodo;
+
  const void *cdb_key;
+
  unsigned cdb_klen;
+
};
+

+
int cdb_findinit(struct cdb_find *cdbfp, struct cdb *cdbp,
+
                 const void *key, unsigned klen);
+
int cdb_findnext(struct cdb_find *cdbfp);
+

+
#define cdb_seqinit(cptr, cdbp) ((*(cptr))=2048)
+
int cdb_seqnext(unsigned *cptr, struct cdb *cdbp);
+

+
/* old simple interface */
+
/* open file using standard routine, then: */
+
int cdb_seek(int fd, const void *key, unsigned klen, unsigned *dlenp);
+
int cdb_bread(int fd, void *buf, int len);
+

+
/* cdb_make */
+

+
struct cdb_make {
+
  int cdb_fd;			/* file descriptor */
+
  /* private */
+
  unsigned cdb_dpos;		/* data position so far */
+
  unsigned cdb_rcnt;		/* record count so far */
+
  unsigned char cdb_buf[4096];	/* write buffer */
+
  unsigned char *cdb_bpos;	/* current buf position */
+
  struct cdb_rl *cdb_rec[256];	/* list of arrays of record infos */
+
};
+

+
enum cdb_put_mode {
+
  CDB_PUT_ADD = 0,	/* add unconditionnaly, like cdb_make_add() */
+
#define CDB_PUT_ADD	CDB_PUT_ADD
+
  CDB_FIND = CDB_PUT_ADD,
+
  CDB_PUT_REPLACE,	/* replace: do not place to index OLD record */
+
#define CDB_PUT_REPLACE	CDB_PUT_REPLACE
+
  CDB_FIND_REMOVE = CDB_PUT_REPLACE,
+
  CDB_PUT_INSERT,	/* add only if not already exists */
+
#define CDB_PUT_INSERT	CDB_PUT_INSERT
+
  CDB_PUT_WARN,		/* add unconditionally but ret. 1 if exists */
+
#define CDB_PUT_WARN	CDB_PUT_WARN
+
  CDB_PUT_REPLACE0,	/* if a record exists, fill old one with zeros */
+
#define CDB_PUT_REPLACE0 CDB_PUT_REPLACE0
+
  CDB_FIND_FILL0 = CDB_PUT_REPLACE0
+
};
+

+
int cdb_make_start(struct cdb_make *cdbmp, int fd);
+
int cdb_make_add(struct cdb_make *cdbmp,
+
                 const void *key, unsigned klen,
+
                 const void *val, unsigned vlen);
+
int cdb_make_exists(struct cdb_make *cdbmp,
+
                    const void *key, unsigned klen);
+
int cdb_make_find(struct cdb_make *cdbmp,
+
                  const void *key, unsigned klen,
+
                  enum cdb_put_mode mode);
+
int cdb_make_put(struct cdb_make *cdbmp,
+
                 const void *key, unsigned klen,
+
                 const void *val, unsigned vlen,
+
                 enum cdb_put_mode mode);
+
int cdb_make_finish(struct cdb_make *cdbmp);
+

+
#ifdef __cplusplus
+
} /* extern "C" */
+
#endif
+

+
#endif /* include guard */
added external/tinycdb/cdb_find.c
@@ -0,0 +1,76 @@
+
/* $Id: cdb_find.c,v 1.8 2003-11-03 16:42:41 mjt Exp $
+
 * cdb_find routine
+
 *
+
 * This file is a part of tinycdb package by Michael Tokarev, mjt@corpit.ru.
+
 * Public domain.
+
 */
+

+
#include "cdb_int.h"
+

+
int
+
cdb_find(struct cdb *cdbp, const void *key, unsigned klen)
+
{
+
  const unsigned char *htp;	/* hash table pointer */
+
  const unsigned char *htab;	/* hash table */
+
  const unsigned char *htend;	/* end of hash table */
+
  unsigned httodo;		/* ht bytes left to look */
+
  unsigned pos, n;
+

+
  unsigned hval;
+

+
  if (klen >= cdbp->cdb_dend)	/* if key size is too large */
+
    return 0;
+

+
  hval = cdb_hash(key, klen);
+

+
  /* find (pos,n) hash table to use */
+
  /* first 2048 bytes (toc) are always available */
+
  /* (hval % 256) * 8 */
+
  htp = cdbp->cdb_mem + ((hval << 3) & 2047); /* index in toc (256x8) */
+
  n = cdb_unpack(htp + 4);	/* table size */
+
  if (!n)			/* empty table */
+
    return 0;			/* not found */
+
  httodo = n << 3;		/* bytes of htab to lookup */
+
  pos = cdb_unpack(htp);	/* htab position */
+
  if (n > (cdbp->cdb_fsize >> 3) /* overflow of httodo ? */
+
      || pos < cdbp->cdb_dend /* is htab inside data section ? */
+
      || pos > cdbp->cdb_fsize /* htab start within file ? */
+
      || httodo > cdbp->cdb_fsize - pos) /* entrie htab within file ? */
+
    return errno = EPROTO, -1;
+

+
  htab = cdbp->cdb_mem + pos;	/* htab pointer */
+
  htend = htab + httodo;	/* after end of htab */
+
  /* htab starting position: rest of hval modulo htsize, 8bytes per elt */
+
  htp = htab + (((hval >> 8) % n) << 3);
+

+
  for(;;) {
+
    pos = cdb_unpack(htp + 4);	/* record position */
+
    if (!pos)
+
      return 0;
+
    if (cdb_unpack(htp) == hval) {
+
      if (pos > cdbp->cdb_dend - 8) /* key+val lengths */
+
	return errno = EPROTO, -1;
+
      if (cdb_unpack(cdbp->cdb_mem + pos) == klen) {
+
	if (cdbp->cdb_dend - klen < pos + 8)
+
	  return errno = EPROTO, -1;
+
	if (memcmp(key, cdbp->cdb_mem + pos + 8, klen) == 0) {
+
	  n = cdb_unpack(cdbp->cdb_mem + pos + 4);
+
	  pos += 8;
+
	  if (cdbp->cdb_dend < n || cdbp->cdb_dend - n < pos + klen)
+
	    return errno = EPROTO, -1;
+
	  cdbp->cdb_kpos = pos;
+
	  cdbp->cdb_klen = klen;
+
	  cdbp->cdb_vpos = pos + klen;
+
	  cdbp->cdb_vlen = n;
+
	  return 1;
+
	}
+
      }
+
    }
+
    httodo -= 8;
+
    if (!httodo)
+
      return 0;
+
    if ((htp += 8) >= htend)
+
      htp = htab;
+
  }
+

+
}
added external/tinycdb/cdb_findnext.c
@@ -0,0 +1,81 @@
+
/* $Id: cdb_findnext.c,v 1.9 2003-11-03 16:42:41 mjt Exp $
+
 * sequential cdb_find routines
+
 *
+
 * This file is a part of tinycdb package by Michael Tokarev, mjt@corpit.ru.
+
 * Public domain.
+
 */
+

+
/* see cdb_find.c for comments */
+

+
#include "cdb_int.h"
+

+
int
+
cdb_findinit(struct cdb_find *cdbfp, struct cdb *cdbp,
+
             const void *key, unsigned klen)
+
{
+
  unsigned n, pos;
+

+
  cdbfp->cdb_cdbp = cdbp;
+
  cdbfp->cdb_key = key;
+
  cdbfp->cdb_klen = klen;
+
  cdbfp->cdb_hval = cdb_hash(key, klen);
+

+
  cdbfp->cdb_htp = cdbp->cdb_mem + ((cdbfp->cdb_hval << 3) & 2047);
+
  n = cdb_unpack(cdbfp->cdb_htp + 4);
+
  cdbfp->cdb_httodo = n << 3;
+
  if (!n)
+
    return 0;
+
  pos = cdb_unpack(cdbfp->cdb_htp);
+
  if (n > (cdbp->cdb_fsize >> 3)
+
      || pos < cdbp->cdb_dend
+
      || pos > cdbp->cdb_fsize
+
      || cdbfp->cdb_httodo > cdbp->cdb_fsize - pos)
+
    return errno = EPROTO, -1;
+

+
  cdbfp->cdb_htab = cdbp->cdb_mem + pos;
+
  cdbfp->cdb_htend = cdbfp->cdb_htab + cdbfp->cdb_httodo;
+
  cdbfp->cdb_htp = cdbfp->cdb_htab + (((cdbfp->cdb_hval >> 8) % n) << 3);
+

+
  return 1;
+
}
+

+
int
+
cdb_findnext(struct cdb_find *cdbfp) {
+
  struct cdb *cdbp = cdbfp->cdb_cdbp;
+
  unsigned pos, n;
+
  unsigned klen = cdbfp->cdb_klen;
+

+
  while(cdbfp->cdb_httodo) {
+
    pos = cdb_unpack(cdbfp->cdb_htp + 4);
+
    if (!pos)
+
      return 0;
+
    n = cdb_unpack(cdbfp->cdb_htp) == cdbfp->cdb_hval;
+
    if ((cdbfp->cdb_htp += 8) >= cdbfp->cdb_htend)
+
      cdbfp->cdb_htp = cdbfp->cdb_htab;
+
    cdbfp->cdb_httodo -= 8;
+
    if (n) {
+
      if (pos > cdbp->cdb_fsize - 8)
+
	return errno = EPROTO, -1;
+
      if (cdb_unpack(cdbp->cdb_mem + pos) == klen) {
+
	if (cdbp->cdb_fsize - klen < pos + 8)
+
	  return errno = EPROTO, -1;
+
	if (memcmp(cdbfp->cdb_key,
+
	    cdbp->cdb_mem + pos + 8, klen) == 0) {
+
	  n = cdb_unpack(cdbp->cdb_mem + pos + 4);
+
	  pos += 8;
+
	  if (cdbp->cdb_fsize < n ||
+
              cdbp->cdb_fsize - n < pos + klen)
+
	    return errno = EPROTO, -1;
+
	  cdbp->cdb_kpos = pos;
+
	  cdbp->cdb_klen = klen;
+
	  cdbp->cdb_vpos = pos + klen;
+
	  cdbp->cdb_vlen = n;
+
	  return 1;
+
	}
+
      }
+
    }
+
  }
+

+
  return 0;
+

+
}
added external/tinycdb/cdb_hash.c
@@ -0,0 +1,19 @@
+
/* $Id: cdb_hash.c,v 1.5 2003-11-03 16:42:41 mjt Exp $
+
 * cdb hashing routine
+
 *
+
 * This file is a part of tinycdb package by Michael Tokarev, mjt@corpit.ru.
+
 * Public domain.
+
 */
+

+
#include "cdb.h"
+

+
unsigned
+
cdb_hash(const void *buf, unsigned len)
+
{
+
  register const unsigned char *p = (const unsigned char *)buf;
+
  register const unsigned char *end = p + len;
+
  register unsigned hash = 5381;	/* start value */
+
  while (p < end)
+
    hash = (hash + (hash << 5)) ^ *p++;
+
  return hash;
+
}
added external/tinycdb/cdb_init.c
@@ -0,0 +1,113 @@
+
/* $Id: cdb_init.c,v 1.12 2008-11-06 18:07:04 mjt Exp $
+
 * cdb_init, cdb_free and cdb_read routines
+
 *
+
 * This file is a part of tinycdb package by Michael Tokarev, mjt@corpit.ru.
+
 * Public domain.
+
 */
+

+
#include <sys/types.h>
+
#ifdef _WIN32
+
# include <windows.h>
+
#else
+
# include <sys/mman.h>
+
# ifndef MAP_FAILED
+
#  define MAP_FAILED ((void*)-1)
+
# endif
+
#endif
+
#include <sys/stat.h>
+
#include "cdb_int.h"
+

+
int
+
cdb_init(struct cdb *cdbp, int fd)
+
{
+
  struct stat st;
+
  unsigned char *mem;
+
  unsigned fsize, dend;
+
#ifdef _WIN32
+
  HANDLE hFile, hMapping;
+
#endif
+

+
  /* get file size */
+
  if (fstat(fd, &st) < 0)
+
    return -1;
+
  /* trivial sanity check: at least toc should be here */
+
  if (st.st_size < 2048)
+
    return errno = EPROTO, -1;
+
  fsize = (unsigned)(st.st_size & 0xffffffffu);
+
  /* memory-map file */
+
#ifdef _WIN32
+
  hFile = (HANDLE) _get_osfhandle(fd);
+
  if (hFile == (HANDLE) -1)
+
    return -1;
+
  hMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
+
  if (!hMapping)
+
    return -1;
+
  mem = (unsigned char *)MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0);
+
  CloseHandle(hMapping);
+
  if (!mem)
+
    return -1;
+
#else
+
  mem = (unsigned char*)mmap(NULL, fsize, PROT_READ, MAP_SHARED, fd, 0);
+
  if (mem == MAP_FAILED)
+
    return -1;
+
#endif /* _WIN32 */
+

+
  cdbp->cdb_fd = fd;
+
  cdbp->cdb_fsize = fsize;
+
  cdbp->cdb_mem = mem;
+

+
#if 0
+
  /* XXX don't know well about madvise syscall -- is it legal
+
     to set different options for parts of one mmap() region?
+
     There is also posix_madvise() exist, with POSIX_MADV_RANDOM etc...
+
  */
+
#ifdef MADV_RANDOM
+
  /* set madvise() parameters. Ignore errors for now if system
+
     doesn't support it */
+
  madvise(mem, 2048, MADV_WILLNEED);
+
  madvise(mem + 2048, cdbp->cdb_fsize - 2048, MADV_RANDOM);
+
#endif
+
#endif
+

+
  cdbp->cdb_vpos = cdbp->cdb_vlen = 0;
+
  cdbp->cdb_kpos = cdbp->cdb_klen = 0;
+
  dend = cdb_unpack(mem);
+
  if (dend < 2048) dend = 2048;
+
  else if (dend >= fsize) dend = fsize;
+
  cdbp->cdb_dend = dend;
+

+
  return 0;
+
}
+

+
void
+
cdb_free(struct cdb *cdbp)
+
{
+
  if (cdbp->cdb_mem) {
+
#ifdef _WIN32
+
    UnmapViewOfFile((void*) cdbp->cdb_mem);
+
#else
+
    munmap((void*)cdbp->cdb_mem, cdbp->cdb_fsize);
+
#endif /* _WIN32 */
+
    cdbp->cdb_mem = NULL;
+
  }
+
  cdbp->cdb_fsize = 0;
+
}
+

+
const void *
+
cdb_get(const struct cdb *cdbp, unsigned len, unsigned pos)
+
{
+
  if (pos > cdbp->cdb_fsize || cdbp->cdb_fsize - pos < len) {
+
    errno = EPROTO;
+
    return NULL;
+
  }
+
  return cdbp->cdb_mem + pos;
+
}
+

+
int
+
cdb_read(const struct cdb *cdbp, void *buf, unsigned len, unsigned pos)
+
{
+
  const void *data = cdb_get(cdbp, len, pos);
+
  if (!data) return -1;
+
  memcpy(buf, data, len);
+
  return 0;
+
}
added external/tinycdb/cdb_int.h
@@ -0,0 +1,41 @@
+
/* $Id: cdb_int.h,v 1.13 2006-06-28 17:49:21 mjt Exp $
+
 * internal cdb library declarations
+
 *
+
 * This file is a part of tinycdb package by Michael Tokarev, mjt@corpit.ru.
+
 * Public domain.
+
 */
+

+
#include "cdb.h"
+
#include <errno.h>
+
#include <string.h>
+

+
#ifndef EPROTO
+
# define EPROTO EINVAL
+
#endif
+

+
#ifndef internal_function
+
# ifdef __GNUC__
+
#  define internal_function __attribute__((visibility("hidden")))
+
# else
+
#  define internal_function
+
# endif
+
#endif
+

+
struct cdb_rec {
+
  unsigned hval;
+
  unsigned rpos;
+
};
+

+
struct cdb_rl {
+
  struct cdb_rl *next;
+
  unsigned cnt;
+
  struct cdb_rec rec[254];
+
};
+

+
int _cdb_make_write(struct cdb_make *cdbmp,
+
		    const unsigned char *ptr, unsigned len);
+
int _cdb_make_fullwrite(int fd, const unsigned char *buf, unsigned len);
+
int _cdb_make_flush(struct cdb_make *cdbmp);
+
int _cdb_make_add(struct cdb_make *cdbmp, unsigned hval,
+
                  const void *key, unsigned klen,
+
                  const void *val, unsigned vlen);
added external/tinycdb/cdb_make.c
@@ -0,0 +1,184 @@
+
/* $Id: cdb_make.c,v 1.13 2009-01-12 21:59:30 mjt Exp $
+
 * basic cdb creation routines
+
 *
+
 * This file is a part of tinycdb package by Michael Tokarev, mjt@corpit.ru.
+
 * Public domain.
+
 */
+

+
#include <unistd.h>
+
#include <stdlib.h>
+
#include <string.h>
+
#include "cdb_int.h"
+

+
void
+
cdb_pack(unsigned num, unsigned char buf[4])
+
{
+
  buf[0] = num & 255; num >>= 8;
+
  buf[1] = num & 255; num >>= 8;
+
  buf[2] = num & 255;
+
  buf[3] = num >> 8;
+
}
+

+
int
+
cdb_make_start(struct cdb_make *cdbmp, int fd)
+
{
+
  memset(cdbmp, 0, sizeof(*cdbmp));
+
  cdbmp->cdb_fd = fd;
+
  cdbmp->cdb_dpos = 2048;
+
  cdbmp->cdb_bpos = cdbmp->cdb_buf + 2048;
+
  return 0;
+
}
+

+
int internal_function
+
_cdb_make_fullwrite(int fd, const unsigned char *buf, unsigned len)
+
{
+
  while(len) {
+
    int l = write(fd, buf, len);
+
    if (l > 0) {
+
      len -= l;
+
      buf += l;
+
    }
+
    else if (l < 0 && errno != EINTR)
+
      return -1;
+
  }
+
  return 0;
+
}
+

+
int internal_function
+
_cdb_make_flush(struct cdb_make *cdbmp) {
+
  unsigned len = cdbmp->cdb_bpos - cdbmp->cdb_buf;
+
  if (len) {
+
    if (_cdb_make_fullwrite(cdbmp->cdb_fd, cdbmp->cdb_buf, len) < 0)
+
      return -1;
+
    cdbmp->cdb_bpos = cdbmp->cdb_buf;
+
  }
+
  return 0;
+
}
+

+
int internal_function
+
_cdb_make_write(struct cdb_make *cdbmp, const unsigned char *ptr, unsigned len)
+
{
+
  unsigned l = sizeof(cdbmp->cdb_buf) - (cdbmp->cdb_bpos - cdbmp->cdb_buf);
+
  cdbmp->cdb_dpos += len;
+
  if (len > l) {
+
    memcpy(cdbmp->cdb_bpos, ptr, l);
+
    cdbmp->cdb_bpos += l;
+
    if (_cdb_make_flush(cdbmp) < 0)
+
      return -1;
+
    ptr += l; len -= l;
+
    l = len / sizeof(cdbmp->cdb_buf);
+
    if (l) {
+
      l *= sizeof(cdbmp->cdb_buf);
+
      if (_cdb_make_fullwrite(cdbmp->cdb_fd, ptr, l) < 0)
+
        return -1;
+
      ptr += l; len -= l;
+
    }
+
  }
+
  if (len) {
+
    memcpy(cdbmp->cdb_bpos, ptr, len);
+
    cdbmp->cdb_bpos += len;
+
  }
+
  return 0;
+
}
+

+
static int
+
cdb_make_finish_internal(struct cdb_make *cdbmp)
+
{
+
  unsigned hcnt[256];		/* hash table counts */
+
  unsigned hpos[256];		/* hash table positions */
+
  struct cdb_rec *htab;
+
  unsigned char *p;
+
  struct cdb_rl *rl;
+
  unsigned hsize;
+
  unsigned t, i;
+

+
  if (((0xffffffff - cdbmp->cdb_dpos) >> 3) < cdbmp->cdb_rcnt)
+
    return errno = ENOMEM, -1;
+

+
  /* count htab sizes and reorder reclists */
+
  hsize = 0;
+
  for (t = 0; t < 256; ++t) {
+
    struct cdb_rl *rlt = NULL;
+
    i = 0;
+
    rl = cdbmp->cdb_rec[t];
+
    while(rl) {
+
      struct cdb_rl *rln = rl->next;
+
      rl->next = rlt;
+
      rlt = rl;
+
      i += rl->cnt;
+
      rl = rln;
+
    }
+
    cdbmp->cdb_rec[t] = rlt;
+
    if (hsize < (hcnt[t] = i << 1))
+
      hsize = hcnt[t];
+
  }
+

+
  /* allocate memory to hold max htable */
+
  htab = (struct cdb_rec*)malloc((hsize + 2) * sizeof(struct cdb_rec));
+
  if (!htab)
+
    return errno = ENOENT, -1;
+
  p = (unsigned char *)htab;
+
  htab += 2;
+

+
  /* build hash tables */
+
  for (t = 0; t < 256; ++t) {
+
    unsigned len, hi;
+
    hpos[t] = cdbmp->cdb_dpos;
+
    if ((len = hcnt[t]) == 0)
+
      continue;
+
    for (i = 0; i < len; ++i)
+
      htab[i].hval = htab[i].rpos = 0;
+
    for (rl = cdbmp->cdb_rec[t]; rl; rl = rl->next)
+
      for (i = 0; i < rl->cnt; ++i) {
+
       hi = (rl->rec[i].hval >> 8) % len;
+
        while(htab[hi].rpos)
+
          if (++hi == len)
+
            hi = 0;
+
        htab[hi] = rl->rec[i];
+
      }
+
    for (i = 0; i < len; ++i) {
+
      cdb_pack(htab[i].hval, p + (i << 3));
+
      cdb_pack(htab[i].rpos, p + (i << 3) + 4);
+
    }
+
    if (_cdb_make_write(cdbmp, p, len << 3) < 0) {
+
      free(p);
+
      return -1;
+
    }
+
  }
+
  free(p);
+
  if (_cdb_make_flush(cdbmp) < 0)
+
    return -1;
+
  p = cdbmp->cdb_buf;
+
  for (t = 0; t < 256; ++t) {
+
    cdb_pack(hpos[t], p + (t << 3));
+
    cdb_pack(hcnt[t], p + (t << 3) + 4);
+
  }
+
  if (lseek(cdbmp->cdb_fd, 0, 0) != 0 ||
+
      _cdb_make_fullwrite(cdbmp->cdb_fd, p, 2048) != 0)
+
    return -1;
+

+
  return 0;
+
}
+

+
static void
+
cdb_make_free(struct cdb_make *cdbmp)
+
{
+
  unsigned t;
+
  for(t = 0; t < 256; ++t) {
+
    struct cdb_rl *rl = cdbmp->cdb_rec[t];
+
    while(rl) {
+
      struct cdb_rl *tm = rl;
+
      rl = rl->next;
+
      free(tm);
+
    }
+
  }
+
}
+

+
int
+
cdb_make_finish(struct cdb_make *cdbmp)
+
{
+
  int r = cdb_make_finish_internal(cdbmp);
+
  cdb_make_free(cdbmp);
+
  return r;
+
}
+

added external/tinycdb/cdb_make_add.c
@@ -0,0 +1,50 @@
+
/* $Id: cdb_make_add.c,v 1.8 2006-06-28 17:49:21 mjt Exp $
+
 * basic cdb_make_add routine
+
 *
+
 * This file is a part of tinycdb package by Michael Tokarev, mjt@corpit.ru.
+
 * Public domain.
+
 */
+

+
#include <stdlib.h> /* for malloc */
+
#include "cdb_int.h"
+

+
int internal_function
+
_cdb_make_add(struct cdb_make *cdbmp, unsigned hval,
+
              const void *key, unsigned klen,
+
              const void *val, unsigned vlen)
+
{
+
  unsigned char rlen[8];
+
  struct cdb_rl *rl;
+
  unsigned i;
+
  if (klen > 0xffffffff - (cdbmp->cdb_dpos + 8) ||
+
      vlen > 0xffffffff - (cdbmp->cdb_dpos + klen + 8))
+
    return errno = ENOMEM, -1;
+
  i = hval & 255;
+
  rl = cdbmp->cdb_rec[i];
+
  if (!rl || rl->cnt >= sizeof(rl->rec)/sizeof(rl->rec[0])) {
+
    rl = (struct cdb_rl*)malloc(sizeof(struct cdb_rl));
+
    if (!rl)
+
      return errno = ENOMEM, -1;
+
    rl->cnt = 0;
+
    rl->next = cdbmp->cdb_rec[i];
+
    cdbmp->cdb_rec[i] = rl;
+
  }
+
  i = rl->cnt++;
+
  rl->rec[i].hval = hval;
+
  rl->rec[i].rpos = cdbmp->cdb_dpos;
+
  ++cdbmp->cdb_rcnt;
+
  cdb_pack(klen, rlen);
+
  cdb_pack(vlen, rlen + 4);
+
  if (_cdb_make_write(cdbmp, rlen, 8) < 0 ||
+
      _cdb_make_write(cdbmp, key, klen) < 0 ||
+
      _cdb_make_write(cdbmp, val, vlen) < 0)
+
    return -1;
+
  return 0;
+
}
+

+
int
+
cdb_make_add(struct cdb_make *cdbmp,
+
             const void *key, unsigned klen,
+
             const void *val, unsigned vlen) {
+
  return _cdb_make_add(cdbmp, cdb_hash(key, klen), key, klen, val, vlen);
+
}
added external/tinycdb/cdb_make_put.c
@@ -0,0 +1,204 @@
+
/* $Id: cdb_make_put.c,v 1.13 2005-04-18 09:46:50 mjt Exp $
+
 * "advanced" cdb_make_put routine
+
 *
+
 * This file is a part of tinycdb package by Michael Tokarev, mjt@corpit.ru.
+
 * Public domain.
+
 */
+

+
#include <stdlib.h>
+
#include <unistd.h>
+
#include <assert.h>
+
#include "cdb_int.h"
+

+
static void
+
fixup_rpos(struct cdb_make *cdbmp, unsigned rpos, unsigned rlen) {
+
  unsigned i;
+
  struct cdb_rl *rl;
+
  register struct cdb_rec *rp, *rs;
+
  for (i = 0; i < 256; ++i) {
+
    for (rl = cdbmp->cdb_rec[i]; rl; rl = rl->next)
+
      for (rs = rl->rec, rp = rs + rl->cnt; --rp >= rs;)
+
        if (rp->rpos <= rpos) goto nexthash;
+
        else rp->rpos -= rlen;
+
nexthash:;
+
  }
+
}
+

+
static int
+
remove_record(struct cdb_make *cdbmp, unsigned rpos, unsigned rlen) {
+
  unsigned pos, len;
+
  int r, fd;
+

+
  len = cdbmp->cdb_dpos - rpos - rlen;
+
  cdbmp->cdb_dpos -= rlen;
+
  if (!len)
+
    return 0;	/* it was the last record, nothing to do */
+
  pos = rpos;
+
  fd = cdbmp->cdb_fd;
+
  do {
+
    r = len > sizeof(cdbmp->cdb_buf) ? sizeof(cdbmp->cdb_buf) : len;
+
    if (lseek(fd, pos + rlen, SEEK_SET) < 0 ||
+
        (r = read(fd, cdbmp->cdb_buf, r)) <= 0)
+
      return -1;
+
    if (lseek(fd, pos, SEEK_SET) < 0 ||
+
        _cdb_make_fullwrite(fd, cdbmp->cdb_buf, r) < 0)
+
      return -1;
+
    pos += r;
+
    len -= r;
+
  } while(len);
+
  assert(cdbmp->cdb_dpos == pos);
+
  fixup_rpos(cdbmp, rpos, rlen);
+
  return 0;
+
}
+

+
static int
+
zerofill_record(struct cdb_make *cdbmp, unsigned rpos, unsigned rlen) {
+
  if (rpos + rlen == cdbmp->cdb_dpos) {
+
    cdbmp->cdb_dpos = rpos;
+
    return 0;
+
  }
+
  if (lseek(cdbmp->cdb_fd, rpos, SEEK_SET) < 0)
+
    return -1;
+
  memset(cdbmp->cdb_buf, 0, sizeof(cdbmp->cdb_buf));
+
  cdb_pack(rlen - 8, cdbmp->cdb_buf + 4);
+
  for(;;) {
+
    rpos = rlen > sizeof(cdbmp->cdb_buf) ? sizeof(cdbmp->cdb_buf) : rlen;
+
    if (_cdb_make_fullwrite(cdbmp->cdb_fd, cdbmp->cdb_buf, rpos) < 0)
+
      return -1;
+
    rlen -= rpos;
+
    if (!rlen) return 0;
+
    memset(cdbmp->cdb_buf + 4, 0, 4);
+
  }
+
}
+

+
/* return: 0 = not found, 1 = error, or record length */
+
static unsigned
+
match(struct cdb_make *cdbmp, unsigned pos, const char *key, unsigned klen)
+
{
+
  int len;
+
  unsigned rlen;
+
  if (lseek(cdbmp->cdb_fd, pos, SEEK_SET) < 0)
+
    return 1;
+
  if (read(cdbmp->cdb_fd, cdbmp->cdb_buf, 8) != 8)
+
    return 1;
+
  if (cdb_unpack(cdbmp->cdb_buf) != klen)
+
    return 0;
+

+
  /* record length; check its validity */
+
  rlen = cdb_unpack(cdbmp->cdb_buf + 4);
+
  if (rlen > cdbmp->cdb_dpos - pos - klen - 8)
+
    return errno = EPROTO, 1;	/* someone changed our file? */
+
  rlen += klen + 8;
+

+
  while(klen) {
+
    len = klen > sizeof(cdbmp->cdb_buf) ? sizeof(cdbmp->cdb_buf) : klen;
+
    len = read(cdbmp->cdb_fd, cdbmp->cdb_buf, len);
+
    if (len <= 0)
+
      return 1;
+
    if (memcmp(cdbmp->cdb_buf, key, len) != 0)
+
      return 0;
+
    key += len;
+
    klen -= len;
+
  }
+

+
  return rlen;
+
}
+

+
static int
+
findrec(struct cdb_make *cdbmp,
+
        const void *key, unsigned klen, unsigned hval,
+
        enum cdb_put_mode mode)
+
{
+
  struct cdb_rl *rl;
+
  struct cdb_rec *rp, *rs;
+
  unsigned r;
+
  int seeked = 0;
+
  int ret = 0;
+
  for(rl = cdbmp->cdb_rec[hval&255]; rl; rl = rl->next)
+
    for(rs = rl->rec, rp = rs + rl->cnt; --rp >= rs;) {
+
      if (rp->hval != hval)
+
	continue;
+
      /*XXX this explicit flush may be unnecessary having
+
       * smarter match() that looks into cdb_buf too, but
+
       * most of a time here spent in finding hash values
+
       * (above), not keys */
+
      if (!seeked && _cdb_make_flush(cdbmp) < 0)
+
        return -1;
+
      seeked = 1;
+
      r = match(cdbmp, rp->rpos, key, klen);
+
      if (!r)
+
	continue;
+
      if (r == 1)
+
	return -1;
+
      ret = 1;
+
      switch(mode) {
+
      case CDB_FIND_REMOVE:
+
        if (remove_record(cdbmp, rp->rpos, r) < 0)
+
          return -1;
+
	break;
+
      case CDB_FIND_FILL0:
+
	if (zerofill_record(cdbmp, rp->rpos, r) < 0)
+
          return -1;
+
	break;
+
      default: goto finish;
+
      }
+
      memmove(rp, rp + 1, (rs + rl->cnt - 1 - rp) * sizeof(*rp));
+
      --rl->cnt;
+
      --cdbmp->cdb_rcnt;
+
  }
+
finish:
+
  if (seeked && lseek(cdbmp->cdb_fd, cdbmp->cdb_dpos, SEEK_SET) < 0)
+
    return -1;
+
  return ret;
+
}
+

+
int
+
cdb_make_find(struct cdb_make *cdbmp,
+
              const void *key, unsigned klen,
+
              enum cdb_put_mode mode)
+
{
+
  return findrec(cdbmp, key, klen, cdb_hash(key, klen), mode);
+
}
+

+
int
+
cdb_make_exists(struct cdb_make *cdbmp,
+
                const void *key, unsigned klen)
+
{
+
  return cdb_make_find(cdbmp, key, klen, CDB_FIND);
+
}
+

+
int
+
cdb_make_put(struct cdb_make *cdbmp,
+
	     const void *key, unsigned klen,
+
	     const void *val, unsigned vlen,
+
	     enum cdb_put_mode mode)
+
{
+
  unsigned hval = cdb_hash(key, klen);
+
  int r;
+

+
  switch(mode) {
+
    case CDB_PUT_REPLACE:
+
    case CDB_PUT_INSERT:
+
    case CDB_PUT_WARN:
+
    case CDB_PUT_REPLACE0:
+
      r = findrec(cdbmp, key, klen, hval, mode);
+
      if (r < 0)
+
        return -1;
+
      if (r && mode == CDB_PUT_INSERT)
+
        return errno = EEXIST, 1;
+
      break;
+

+
    case CDB_PUT_ADD:
+
      r = 0;
+
      break;
+

+
    default:
+
      return errno = EINVAL, -1;
+
  }
+

+
  if (_cdb_make_add(cdbmp, hval, key, klen, val, vlen) < 0)
+
    return -1;
+

+
  return r;
+
}
+

added external/tinycdb/cdb_seek.c
@@ -0,0 +1,100 @@
+
/* $Id: cdb_seek.c,v 1.7 2006-09-03 09:51:25 mjt Exp $
+
 * old interface for reading cdb file
+
 *
+
 * This file is a part of tinycdb package by Michael Tokarev, mjt@corpit.ru.
+
 * Public domain.
+
 */
+

+
#include <unistd.h>
+
#include "cdb_int.h"
+

+
#ifndef SEEK_SET
+
# define SEEK_SET 0
+
#endif
+

+
/* read a chunk from file, ignoring interrupts (EINTR) */
+

+
int
+
cdb_bread(int fd, void *buf, int len)
+
{
+
  int l;
+
  while(len > 0) {
+
    do l = read(fd, buf, len);
+
    while(l < 0 && errno == EINTR);
+
    if (l <= 0) {
+
      if (!l)
+
        errno = EIO;
+
      return -1;
+
    }
+
    buf = (char*)buf + l;
+
    len -= l;
+
  }
+
  return 0;
+
}
+

+
/* find a given key in cdb file, seek a file pointer to it's value and
+
   place data length to *dlenp. */
+

+
int
+
cdb_seek(int fd, const void *key, unsigned klen, unsigned *dlenp)
+
{
+
  unsigned htstart;		/* hash table start position */
+
  unsigned htsize;		/* number of elements in a hash table */
+
  unsigned httodo;		/* hash table elements left to look */
+
  unsigned hti;			/* hash table index */
+
  unsigned pos;			/* position in a file */
+
  unsigned hval;			/* key's hash value */
+
  unsigned char rbuf[64];	/* read buffer */
+
  int needseek = 1;		/* if we should seek to a hash slot */
+

+
  hval = cdb_hash(key, klen);
+
  pos = (hval & 0xff) << 3; /* position in TOC */
+
  /* read the hash table parameters */
+
  if (lseek(fd, pos, SEEK_SET) < 0 || cdb_bread(fd, rbuf, 8) < 0)
+
    return -1;
+
  if ((htsize = cdb_unpack(rbuf + 4)) == 0)
+
    return 0;
+
  hti = (hval >> 8) % htsize;	/* start position in hash table */
+
  httodo = htsize;
+
  htstart = cdb_unpack(rbuf);
+

+
  for(;;) {
+
    if (needseek && lseek(fd, htstart + (hti << 3), SEEK_SET) < 0)
+
      return -1;
+
    if (cdb_bread(fd, rbuf, 8) < 0)
+
      return -1;
+
    if ((pos = cdb_unpack(rbuf + 4)) == 0) /* not found */
+
      return 0;
+

+
    if (cdb_unpack(rbuf) != hval) /* hash value not matched */
+
      needseek = 0;
+
    else { /* hash value matched */
+
      if (lseek(fd, pos, SEEK_SET) < 0 || cdb_bread(fd, rbuf, 8) < 0)
+
	return -1;
+
      if (cdb_unpack(rbuf) == klen) { /* key length matches */
+
	/* read the key from file and compare with wanted */
+
	unsigned l = klen, c;
+
	const char *k = (const char*)key;
+
	if (dlenp)
+
	  *dlenp = cdb_unpack(rbuf + 4); /* save value length */
+
	for(;;) {
+
	  if (!l) /* the whole key read and matches, return */
+
	    return 1;
+
	  c = l > sizeof(rbuf) ? sizeof(rbuf) : l;
+
	  if (cdb_bread(fd, rbuf, c) < 0)
+
	    return -1;
+
	  if (memcmp(rbuf, k, c) != 0) /* no, it differs, stop here */
+
	    break;
+
	  k += c; l -= c;
+
	}
+
      }
+
      needseek = 1; /* we're looked to other place, should seek back */
+
    }
+
    if (!--httodo)
+
      return 0;
+
    if (++hti == htsize) {
+
      hti = 0;
+
      needseek = 1;
+
    }
+
  }
+
}
added external/tinycdb/cdb_seq.c
@@ -0,0 +1,29 @@
+
/* $Id: cdb_seq.c,v 1.8 2003-11-03 20:10:17 mjt Exp $
+
 * sequential record retrieval routines
+
 *
+
 * This file is a part of tinycdb package by Michael Tokarev, mjt@corpit.ru.
+
 * Public domain.
+
 */
+

+
#include "cdb_int.h"
+

+
int
+
cdb_seqnext(unsigned *cptr, struct cdb *cdbp) {
+
  unsigned klen, vlen;
+
  unsigned pos = *cptr;
+
  unsigned dend = cdbp->cdb_dend;
+
  const unsigned char *mem = cdbp->cdb_mem;
+
  if (pos > dend - 8)
+
    return 0;
+
  klen = cdb_unpack(mem + pos);
+
  vlen = cdb_unpack(mem + pos + 4);
+
  pos += 8;
+
  if (dend - klen < pos || dend - vlen < pos + klen)
+
    return errno = EPROTO, -1;
+
  cdbp->cdb_kpos = pos;
+
  cdbp->cdb_klen = klen;
+
  cdbp->cdb_vpos = pos + klen;
+
  cdbp->cdb_vlen = vlen;
+
  *cptr = pos + klen + vlen;
+
  return 1;
+
}
added external/tinycdb/cdb_unpack.c
@@ -0,0 +1,18 @@
+
/* $Id: cdb_unpack.c,v 1.5 2003-11-03 16:42:41 mjt Exp $
+
 * unpack 32bit integer
+
 *
+
 * This file is a part of tinycdb package by Michael Tokarev, mjt@corpit.ru.
+
 * Public domain.
+
 */
+

+
#include "cdb.h"
+

+
unsigned
+
cdb_unpack(const unsigned char buf[4])
+
{
+
  unsigned n = buf[3];
+
  n <<= 8; n |= buf[2];
+
  n <<= 8; n |= buf[1];
+
  n <<= 8; n |= buf[0];
+
  return n;
+
}
added external/tinycdb/debian/.cvsignore
@@ -0,0 +1,4 @@
+
libcdb-dev
+
libcdb1
+
libnss-cdb
+
tinycdb
added external/tinycdb/debian/changelog
@@ -0,0 +1,228 @@
+
tinycdb (0.77) unstable; urgency=low
+

+
  * manpage typo fixes.
+

+
  * in win32, close file mapping handle right after MapViewOfFile(),
+
    instead of doing it in cdb_close().  This eliminates handle leak
+
    on win32 platform.  And this is how mmap() works on unix, too.
+
    Thanks to David Boyce for this.
+

+
  * also for win32 platform, cdb.c (the utility):
+
    - open cdb files in binary mode
+
    - add windowsisms for include files
+
    Also thanks to David Boyce for the fixes.
+

+
  * fixed a bug in _cdb_make_fullwrite() - wrong logic in EINTR
+
    handling.
+
    Thanks to Florian Weimer.
+
    Closes: #511629.
+

+
  * install /usr/lib/pkgconfig/libcdb.pc from debian/rules.
+
    Closes: #446751, #446752.
+

+
  * lintian warnings:
+
    o moved $DH_COMPAT to deban/compat (=4)
+
    o set Standards-Version: 3.8.0 (no changes needed)
+
    o fixed "package package" in libcdb1 description (Closes: #442571)
+
    o s/${Source-Version}/${binary:Version}/
+

+
  * released 0.77
+

+
 -- Michael Tokarev <mjt@corpit.ru>  Sat, 31 Jan 2009 20:12:02 +0300
+

+
tinycdb (0.76) unstable; urgency=low
+

+
  * 0.76 release.
+
    Closes: #342849, #316253, #360129, #383417.
+
    Also closes: #299026, #344572.
+

+
  * manpage spelling fixes, from Claus Assmann <ca+tinycdb (at) esmtp.org>.
+

+
  * little mods to allow compiling tinycdb by C++ compiler,
+
    from Olly Betts <olly (at) survex.com>.
+

+
  * use program_invocation_short_name on GLIBC, (modified) patch
+
    from Dmitry V. Levin  <ldv (at) altlinux.org>
+

+
  * manpage fix (cdb_findnext() prototype),
+
    from Dmitry V. Levin <ldv (at) altlinux.org>
+

+
  * (somewhat silly) GCC-4.x "signedness" warnings fix, modified patch
+
    from Dmitry V. Levin  <ldv (at) altlinux.org>
+

+
  * more signed vs unsigned char* fixes in various places
+

+
  * Makefile: always build libnss_cdb.so with libcdb_pic.a, no nss-shared
+
    target: to avoid extra dependency from /usr/lib/.
+

+
  * Makefile: use map files for lib*.so, with explicit list of exported
+
    symbols.  This, in particular, avoids exporting of libcdb symbols by
+
    libnss_cdb.so.
+

+
  * mark all internal routines as internal_function (defined as
+
    __attribute__((visibility("hidden"))) for GCC)
+

+
  * Makefile: add tests-shared, to use cdb-shared for testing
+

+
  * Makefile: allow to specify which binary (shared vs static) to install
+
    in install target, by using INSTALLPROG variable
+

+
  * Makefile: pass -DNSSCDB_DIR=... to compiler when building .lo files,
+
    to allow setting of system config dir (/etc by default) on command line.
+
    For nss_cdb module.
+

+
  * Makefile: use $(CP) instead of cp, to be able to specify `cp' options
+
    (CP="cp -p")
+

+
  * Makefile: ship debian/ files in `dist' target as well, if not only
+
    for debian/changelog.
+

+
  * Makefile: bumped version to 0.76
+

+
  * Use unlink(tmpname) + open(O_CREAT|O_EXCL) instead of
+
    open(O_CREAT|O_TRUNC) when creating the new CDB file.
+
    And use O_NOFOLLOW if defined.
+
    This also works around some (probably) possible symlink attacks.
+

+
  * Add -p perms option for cdb utility, to specify mode (permission bits)
+
    for newly created .cdb file (default is 0666&umask(), -p forces the given
+
    mode).
+

+
  * allow tmpname (cdb -t) to be `-', to mean no temp file gets created.
+
    Also check if tmpname is the same as database name and don't try to
+
    rename() if it is.
+

+
  * rewrite nss_cdb-Makefile a bit: simplify it, and use more sane
+
    permission scheme for /etc/shadow
+

+
  * bumped Debian Standards-Version to 3.7.2 (no changes necessary).
+

+
  * fixed a typo in cdb_seek.c, -- it segfaulted if passed NULL dlenp
+
    pointer.  Thanks Daiki for the patch.  Closes: #383417
+

+
  * use MAP_FAILED to test mmap() return value, instead of hardcoded -1.
+

+
  * several minor fixes for debian/* files, thanks to Bernhard R. Link.
+

+
  * removed libnss_cdb Debian package, for now
+

+
  * libcdb-dev replaces tinycdb<0.76
+

+
 -- Michael Tokarev <mjt@corpit.ru>  Sat,  9 Sep 2006 13:55:10 +0400
+

+
tinycdb (0.75) unstable; urgency=low
+

+
  * 0.75 release
+

+
  * debian packaging is now back to me.  Thank you Christian for doing
+
    packaging work for me, it is greatly apprecated.
+

+
  * build 4 packages out of the source base:
+
    - shared library libcdb1
+
    - development files libcdb-dev
+
    - utility tinycdb
+
    - nss module nss-cdb
+
    Closes: Debian #360129, #316253.
+

+
  * rewrote cdb_make_put(CDB_PUT_REPLACE) to a) actually replace *all*
+
    duplicates, not just the first one, and b) perform real replace,
+
    by moving tail of .cdb file.  Also, always add new record to the
+
    end of the file, not to the place where a duplicate was found.
+

+
  * add cdb_make_put(CDB_PUT_REPLACE0) to zerofill all duplicates,
+
    which is faster than CDB_PUT_REPLACE but less accurate as it
+
    leaves gaps in the file.
+

+
  * ship libcdb_pic.a in the -dev package.
+

+
 -- Michael Tokarev <mjt@corpit.ru>  Tue, 23 Aug 2005 20:06:01 +0400
+

+
tinycdb (0.74-1) unstable; urgency=low
+

+
  * New upstream release.
+

+
 -- Christian Kurz <shorty@debian.org>  Wed, 28 Jul 2004 20:12:02 +0200
+

+
tinycdb (0.73-1) unstable; urgency=low
+

+
  * First Debian release, based on the debian directory that
+
    Michael Tokarev, the upstream author, had provided in the 
+
    package itself. So only some minor changes were necessary.
+

+
 -- Christian Kurz <shorty@debian.org>  Tue,  5 Nov 2002 10:36:59 +0100
+

+
tinycdb (0.72) unstable; urgency=low
+

+
  * cleaned up debian packaging and made it actually work
+
  * no code changes
+

+
 -- Michael Tokarev <mjt@corpit.ru>  Sun, 13 Oct 2002 03:00:32 +0400
+

+
tinycdb (0.71) unstable; urgency=low
+

+
  * rearranged object files to not depend on ranlib on
+
    systems that requires it (i.e. OpenBSD)
+
  * use ranlib but mark it's possible error as non-fatal
+

+
 -- Michael Tokarev <mjt@corpit.ru>  Mon, 22 Jul 2002 13:35:14 +0400
+

+
tinycdb (0.7a) unstable; urgency=low
+

+
  * converted into CVS, added two missing #include <stdlib.h> for
+
    malloc declaration and spec target to the Makefile
+

+
 -- Michael Tokarev <mjt@corpit.ru>  Mon, 10 Dec 2001 00:21:17 +0300
+

+
tinycdb (0.7) unstable; urgency=low
+

+
  * added cdb_seek() and cdb_bread() routines as found
+
    in freecdb/cdb-0.64
+

+
 -- Michael Tokarev <mjt@corpit.ru>  Sun, 14 Oct 2001 19:29:33 +0400
+

+
tinycdb (0.6) unstable; urgency=low
+

+
  * added another option, CDB_PUT_WARN, to cdb_make_put's
+
    flags (to allow adding unconditionally but still warn
+
    about dups), now cdb_make_put seems to be logically
+
    complete.
+
  * added and documented -r and -u options for cdb(1)
+
    command, and made them consistent with -w and -e
+
    also.
+
  * reorganized cdb(1) manpage and added changes made to
+
    cdb command.
+
  * added version references to manpages (and make them
+
    autogenerated to simplify maintenance).
+
  * added cdb(5) manpage describing CDB file format.
+

+
 -- Michael Tokarev <mjt@corpit.ru>  Thu, 26 Jul 2001 02:38:08 +0400
+

+
tinycdb (0.5) unstable; urgency=low
+

+
  * added missing #include <sys/types.h> in cdb_init.c,
+
    thanks to ppetru@ppetru.net (Petru Paler)
+
  * removed usage of pread() in cdb_make_find() and friends,
+
    suggested by Liviu Daia <Liviu.Daia@imar.ro>
+
  * autogenerate tinycdb.spec file from template and debian/changelog
+
  * autogenerate cdb.h from cdb.h.in (substituting version)
+

+
 -- Michael Tokarev <mjt@corpit.ru>  Wed, 25 Jul 2001 17:16:39 +0400
+

+
tinycdb (0.4) unstable; urgency=low
+

+
  * added cdb_make_put() routine to conditionnaly add a record
+
  * split cdb library to more files (finer granularity)
+
  * added cdb_findinit() and cdb_findnext() routines
+
  * renamed cdbtool to cdb
+
  * simplified cdb utility (dropped various format spec, changed options
+
    parsing) and a manpage
+
  * added small note and copyright to every file in package
+
  * added some testsuite (make test)
+

+
 -- Michael Tokarev <mjt@corpit.ru>  Fri, 29 Jun 2001 23:39:53 +0400
+

+
tinycdb (0.3) unstable; urgency=low
+

+
  * Initial Release.
+

+
 -- Michael Tokarev <mjt@corpit.ru>  Sun, 27 May 2001 16:38:58 +0400
added external/tinycdb/debian/compat
@@ -0,0 +1 @@
+
4
added external/tinycdb/debian/control
@@ -0,0 +1,43 @@
+
Source: tinycdb
+
Section: utils
+
Priority: optional
+
Maintainer: Michael Tokarev <mjt@corpit.ru>
+
Build-Depends: debhelper (>= 4)
+
Standards-Version: 3.8.0
+

+
Package: tinycdb
+
Architecture: any
+
Depends: ${shlibs:Depends}
+
Description: an utility to manipulate constant databases (cdb)
+
 tinycdb is a small, fast and reliable utility and subroutine
+
 library for creating and reading constant databases. The database
+
 structure is tuned for fast reading.
+
 .
+
 This package contains a command-line utility to create, analyze, dump
+
 and query cdb files.
+

+
Package: libcdb1
+
Architecture: any
+
Section: libs
+
Depends: ${shlibs:Depends}
+
Description: shared library for constant databases (cdb)
+
 tinycdb is a small, fast and reliable utility and subroutine
+
 library for creating and reading constant databases. The database
+
 structure is tuned for fast reading.
+
 .
+
 This package provides a shared library needed to run
+
 programs using it.
+

+
Package: libcdb-dev
+
Architecture: any
+
Section: libdevel
+
Depends: libcdb1 (= ${binary:Version})
+
Recommends: tinycdb
+
Replaces: tinycdb (<< 0.75)
+
Description: development files for constant databases (cdb)
+
 tinycdb is a small, fast and reliable utility and subroutine
+
 library for creating and reading constant databases. The database
+
 structure is tuned for fast reading.
+
 .
+
 This package provides development files needed
+
 to build programs using cdb library.
added external/tinycdb/debian/control.nss
@@ -0,0 +1,14 @@
+

+
Package: libnss-cdb
+
Architecture: any
+
Section: libs
+
Depends: ${shlibs:Depends}, tinycdb
+
Description: cdb-based nss (name service switch) module
+
 tinycdb is a small, fast and reliable utility and subroutine
+
 library for creating and reading constant databases. The database
+
 structure is tuned for fast reading.
+
 .
+
 This package provides a name service switch (nsswitch) module
+
 to use to index /etc/password files for fast access.
+
 This module works only for passwd, group and shadow databases,
+
 not for hosts, networks, services and others.
added external/tinycdb/debian/copyright
@@ -0,0 +1,4 @@
+
This software was written by Michael Tokarev <mjt@corpit.ru>,
+
based on ideas by Dan Bernstein.  It is in the public domain.
+

+
Source code and home page is at http://www.corpit.ru/mjt/tinycdb.html
added external/tinycdb/debian/libcdb.pc
@@ -0,0 +1,12 @@
+
# Package Information for pkg-config
+

+
prefix=/usr
+
exec_prefix=${prefix}
+
libdir=${exec_prefix}/lib
+
includedir=${prefix}/include
+

+
Name: libcdb
+
Description: tinycdb - Constant Data Base library
+
Version: 0.77
+
Libs: -L${libdir} -lcdb
+
Cflags: -I${includedir}
added external/tinycdb/debian/rules
@@ -0,0 +1,86 @@
+
#!/usr/bin/make -f
+
# -*- makefile -*-
+

+
CFLAGS = -Wall -W -g
+
# can't use -Wmissing-prototypes due to nss routines
+

+
INSTALL = install
+
INSTALL_PROGRAM = $(INSTALL) -p
+
INSTALL_DATA = $(INSTALL) -p -m0644
+

+
ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS)))
+
	CFLAGS += -O0
+
else
+
	CFLAGS += -O2
+
endif
+
ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS)))
+
	INSTALL_PROGRAM += -s
+
endif
+
ifeq (,$(findstring debug,$(DEB_BUILD_OPTIONS)))
+
	CDEFS += -DNDEBUG
+
endif
+

+
SOVER = 1
+

+
configure:	# nothing
+
	dh_testdir
+

+
build:	build-stamp
+
build-stamp:
+
	dh_testdir
+
	$(MAKE) CFLAGS="$(CFLAGS)" \
+
		staticlib sharedlib cdb-shared nss
+
	cp -pf cdb-shared cdb
+
	cp -pf nss_cdb-Makefile cdb-Makefile
+
	touch $@
+

+
clean:
+
	dh_testdir
+
	rm -f build-stamp cdb-Makefile
+
	$(MAKE) distclean
+
	dh_clean
+

+
install: build
+
	dh_testdir
+
	dh_testroot
+
	dh_clean
+
	dh_installdirs
+
	dh_installdocs -A NEWS
+

+
# libcdb$(SOVER)
+
	dh_install -plibcdb$(SOVER) libcdb.so.$(SOVER) usr/lib
+

+
# libcdb-dev
+
	dh_install -plibcdb-dev libcdb.a libcdb_pic.a libcdb.so usr/lib
+
	dh_install -plibcdb-dev cdb.h usr/include
+
	dh_install -plibcdb-dev debian/libcdb.pc usr/lib/pkgconfig
+
	dh_installman -plibcdb-dev cdb.3
+
#	dh_installdocs -plibcdb-dev TODO
+

+
# tinycdb
+
	dh_install -ptinycdb cdb usr/bin
+
	dh_installman -ptinycdb cdb.1 cdb.5
+

+
# libnss-cdb
+
#	dh_install -plibnss-cdb cdb-Makefile etc
+
#	dh_install -plibnss-cdb libnss_cdb.so.2 lib
+

+
binary-indep: build install
+

+
binary-arch: build install
+
	dh_testdir
+
	dh_testroot
+
	dh_installchangelogs
+
	dh_installdocs
+
	dh_strip
+
	dh_compress
+
	dh_fixperms
+
	dh_makeshlibs
+
	dh_installdeb
+
	dh_shlibdeps -L libcdb$(SOVER) -l debian/libcdb$(SOVER)/usr/lib
+
	dh_gencontrol
+
	dh_md5sums
+
	dh_builddeb
+

+
binary: binary-indep binary-arch
+
.PHONY: build clean binary-indep binary-arch binary install configure
added external/tinycdb/libcdb.map
@@ -0,0 +1,26 @@
+
# $Id: libcdb.map,v 1.1 2006-06-28 13:25:46 mjt Exp $
+
# libcdb symbol map file for GNU LD
+
{
+
  global:
+
    cdb_hash;
+
    cdb_unpack;
+
    cdb_pack;
+
    cdb_init;
+
    cdb_free;
+
    cdb_read;
+
    cdb_get;
+
    cdb_find;
+
    cdb_findinit;
+
    cdb_findnext;
+
    cdb_seqnext;
+
    cdb_seek;
+
    cdb_bread;
+
    cdb_make_start;
+
    cdb_make_add;
+
    cdb_make_exists;
+
    cdb_make_put;
+
    cdb_make_find;
+
    cdb_make_finish;
+
  local:
+
    *;
+
};
added external/tinycdb/nss_cdb-Makefile
@@ -0,0 +1,48 @@
+
# $Id: nss_cdb-Makefile,v 1.2 2006-06-28 15:12:02 mjt Exp $
+
# Makefile to create cdb-indexed files for nss_cdb module from
+
# /etc/group, /etc/passwd, /etc/shadow.
+
#
+
# This file is a part of tinycdb package by Michael Tokarev, mjt@corpit.ru.
+
# Public domain.
+

+
AWK = awk
+
SRC = .
+
DST = .
+

+
all: $(DST)/passwd.cdb $(DST)/group.cdb $(DST)/shadow.cdb
+

+
$(DST)/passwd.cdb: $(SRC)/passwd
+
	umask 022; $(AWK) -F: '\
+
/^#/ { next } \
+
NF == 7 { print $$1" "$$0; print ":"$$3" "$$1 } \
+
' $(SRC)/passwd > $@.in
+
	cdb -c -m $@ $@.in
+
	rm -f $@.in
+

+
$(DST)/group.cdb: $(SRC)/group
+
	umask 022; $(AWK) -F: '\
+
/^#/ { next } \
+
NF == 4 { print $$1" "$$0; print ":"$$3" "$$1 } \
+
' $(SRC)/group > $@.in
+
	cdb -c -m $@ $@.in
+
	rm -f $@.in
+

+
# for shadow, we first create all files with mode 0600,
+
# and only when everything's done, right before final
+
# rename (which is done explicitly), we change permissions
+
# and ownership to the right values.  Assuming parent dirs
+
# have proper permissions (so no symlink attacks etc are
+
# possible)
+
$(DST)/shadow.cdb: $(SRC)/shadow
+
	set -e; \
+
	umask 077; \
+
	rm -f $@.in; \
+
	$(AWK) -F: '\
+
/^#/ { next } \
+
NF == 9 { print $$1" "$$0 } \
+
' $(SRC)/shadow > $@.in
+
	cdb -c -m -t $@.tmp -p 0600 $@.tmp2 $@.in
+
	rm -f $@.in
+
	chown --reference=$(SRC)/shadow $@.tmp2
+
	chmod --reference=$(SRC)/shadow $@.tmp2
+
	mv -f $@.tmp2 $@
added external/tinycdb/nss_cdb-group.c
@@ -0,0 +1,101 @@
+
/* $Id: nss_cdb-group.c,v 1.1 2003-11-03 18:23:42 mjt Exp $
+
 * nss_cdb group database routines.
+
 *
+
 * This file is a part of tinycdb package by Michael Tokarev, mjt@corpit.ru.
+
 * Public domain.
+
 */
+

+
#include "nss_cdb.h"
+
#include <grp.h>
+
#include <stdlib.h>
+
#include <string.h>
+

+
nss_common(group, struct group, grent);
+
nss_getbyname(getgrnam, struct group);
+
nss_getbyid(getgrgid, struct group, gid_t);
+

+
static char *getmember(char **bp) {
+
  char *b, *m;
+
  b = *bp;
+
  while(*b == ',') ++b;
+
  if (!*b) return NULL;
+
  m = b++;
+
  for(;;) {
+
    if (*b == ',') {
+
      *b++ = '\0';
+
      *bp = b;
+
      return m;
+
    }
+
    else if (*b == '\0') {
+
      *bp = b;
+
      return m;
+
    }
+
    else
+
      ++b;
+
  }
+
}
+

+
static int
+
nss_group_parse(struct group *result, char *buf, size_t bufl) {
+
  char *bufend;
+
  char **mem;
+
  int n;
+

+
  bufend = buf + strlen(buf) + 1;
+
  n = (unsigned)bufend % sizeof(char*);
+
  if (n)
+
    bufend += sizeof(char*) - n;
+
  result->gr_mem = mem = (char**)bufend;
+

+
  bufend = buf + bufl - sizeof(char*);
+

+
  STRING_FIELD(buf, result->gr_name);
+
  if (!result->gr_name[0]) return -1;
+
  STRING_FIELD(buf, result->gr_passwd);
+
  INT_FIELD(buf, result->gr_gid, (gid_t));
+

+
  for(;;) {
+
    if ((char*)mem > bufend)
+
      return 0;
+
    if (!(*mem++ = getmember(&buf)))
+
      break;
+
  }
+

+
  return 1;
+
}
+

+
#ifdef TEST
+
#include <stdio.h>
+

+
void printit(struct group *g) {
+
  char **mem;
+
  printf("name=%s pass=%s gid=%d mem:", g->gr_name, g->gr_passwd, g->gr_gid);
+
  mem = g->gr_mem;
+
  if (!mem)
+
    printf(" none");
+
  else
+
    while(*mem)
+
      printf(" %s", *mem++);
+
  putchar('\n');
+
}
+

+
int main(int argc, char **argv) {
+
  struct group gr, *g;
+
  char buf[36];
+
  int err, r;
+
  while(*++argv) {
+
    r = _nss_cdb_getgrgid_r(atoi(*argv), &gr, buf, sizeof(buf), &err);
+
    if (r == NSS_STATUS_SUCCESS)
+
      printit(&gr);
+
    else
+
      printf("cdb(%s): %d %s\n", *argv, r, strerror(err));
+
    g = getgrgid(atoi(*argv));
+
    if (g)
+
      printit(g);
+
    else
+
      printf("grgid(%s): %m\n", *argv);
+
  }
+
  return 0;
+
}
+

+
#endif
added external/tinycdb/nss_cdb-passwd.c
@@ -0,0 +1,57 @@
+
/* $Id: nss_cdb-passwd.c,v 1.1 2003-11-03 18:23:42 mjt Exp $
+
 * nss_cdb passwd database routines.
+
 *
+
 * This file is a part of tinycdb package by Michael Tokarev, mjt@corpit.ru.
+
 * Public domain.
+
 */
+

+
#include "nss_cdb.h"
+
#include <pwd.h>
+
#include <stdlib.h>
+

+
nss_common(passwd, struct passwd, pwent);
+
nss_getbyname(getpwnam, struct passwd);
+
nss_getbyid(getpwuid, struct passwd, uid_t);
+

+
static int
+
nss_passwd_parse(struct passwd *result, char *buf, size_t bufl) {
+

+
  STRING_FIELD(buf, result->pw_name);
+
  if (!result->pw_name[0]) return -1;
+
  STRING_FIELD(buf, result->pw_passwd);
+
  INT_FIELD(buf, result->pw_uid, (uid_t));
+
  INT_FIELD(buf, result->pw_gid, (gid_t));
+
  STRING_FIELD(buf, result->pw_gecos);
+
  STRING_FIELD(buf, result->pw_dir);
+
  result->pw_shell = buf;
+

+
  bufl = bufl;
+

+
  return 1;
+
}
+

+
#ifdef TEST
+
#include <stdio.h>
+

+
static void printit(const struct passwd *p) {
+
  printf("name=`%s' pass=`%s' uid=%d gid=%d gecos=`%s' dir=`%s' shell=`%s'\n",
+
	 p->pw_name, p->pw_passwd, p->pw_uid, p->pw_gid, p->pw_gecos, p->pw_dir, p->pw_shell);
+
}
+

+
int main(int argc, char **argv) {
+
  struct passwd pw, *p;
+
  char buf[1024];
+
  int err, r;
+
  while(*++argv) {
+
    r = _nss_cdb_getpwuid_r(atoi(*argv), &pw, buf, sizeof(buf), &err);
+
    if (r == NSS_STATUS_SUCCESS)
+
      printit(&pw);
+
    else
+
      printf("cdb(%s): %d %s\n", *argv, r, strerror(err));
+
    p = getpwuid(atoi(*argv));
+
    if (p) printit(p);
+
    else printf("pwuid(%s): %m\n", *argv);
+
  }
+
  return 0;
+
}
+
#endif
added external/tinycdb/nss_cdb-spwd.c
@@ -0,0 +1,36 @@
+
/* $Id: nss_cdb-spwd.c,v 1.1 2003-11-03 18:23:42 mjt Exp $
+
 * nss_cdb shadow passwd database routines.
+
 *
+
 * This file is a part of tinycdb package by Michael Tokarev, mjt@corpit.ru.
+
 * Public domain.
+
 */
+

+
#include "nss_cdb.h"
+
#include <shadow.h>
+

+
nss_common(shadow, struct spwd, spent);
+
nss_getbyname(getspnam, struct spwd);
+

+
static int
+
nss_shadow_parse(struct spwd *result, char *buf, size_t bufl) {
+

+
  STRING_FIELD(buf, result->sp_namp);
+
  if (!result->sp_namp[0]) return -1;
+
  STRING_FIELD(buf, result->sp_pwdp);
+
  INT_FIELD_MAYBE_NULL(buf, result->sp_lstchg, (long), -1);
+
  INT_FIELD_MAYBE_NULL(buf, result->sp_min, (long), -1);
+
  INT_FIELD_MAYBE_NULL(buf, result->sp_max, (long), -1);
+
  INT_FIELD_MAYBE_NULL(buf, result->sp_warn, (long), -1);
+
  INT_FIELD_MAYBE_NULL(buf, result->sp_inact, (long), -1);
+
  INT_FIELD_MAYBE_NULL(buf, result->sp_expire, (long), -1);
+
  if (*buf) {
+
    result->sp_flag = strtoul(buf, &buf, 10);
+
    if (*buf) return -1;
+
  }
+
  else
+
    result->sp_flag = ~0ul;
+

+
  bufl = bufl;
+

+
  return 1;
+
}
added external/tinycdb/nss_cdb.c
@@ -0,0 +1,209 @@
+
/* $Id: nss_cdb.c,v 1.5 2006-06-28 17:49:21 mjt Exp $
+
 * nss_cdb common routines.
+
 *
+
 * This file is a part of tinycdb package by Michael Tokarev, mjt@corpit.ru.
+
 * Public domain.
+
 */
+

+
#include "nss_cdb.h"
+
#include "cdb_int.h"	/* for internal_function */
+
#include <string.h>
+
#include <unistd.h>
+
#include <fcntl.h>
+
#include <errno.h>
+
#include <stdio.h>
+

+
#if __GLIBC__ /* XXX this is in fact not a right condition */
+
/* XXX on glibc, this stuff works due to linker/libpthreads stubs/tricks.
+
 * On other libcs, it may require linking whole -lpthread, which is
+
 * not a good thing to do for nss module...
+
 */
+

+
#include <pthread.h>
+

+
#define lock_define(class,name) \
+
  class pthread_mutex_t name = PTHREAD_MUTEX_INITIALIZER;
+
#define lock_lock(name) pthread_mutex_lock(&(name))
+
#define lock_unlock(name) pthread_mutex_unlock(&(name))
+

+
#else /* !__GNU_LIBRARY__ */
+

+
# define lock_define_initialized(class,name)
+
# define lock_lock(name)
+
# define lock_unlock(name)
+

+
#endif /* __GNU_LIBRARY__ */
+

+
lock_define(static, lock)
+

+
/* General principle: we skip invalid/unparseable entries completely,
+
 * as if there was no such entry at all (returning NOTFOUND).
+
 * In case of data read error (e.g. invalid .cdb structure), we
+
 * return UNAVAIL.
+
 */
+

+
#define isopen(dbp) ((dbp)->lastpos)
+

+
static int
+
__nss_cdb_dosetent(struct nss_cdb *dbp) {
+
  int fd;
+

+
  fd = open(dbp->dbname, O_RDONLY);
+
  if (fd < 0)
+
    return 0;
+
  if (cdb_init(&dbp->cdb, fd) != 0) {
+
    close(fd);
+
    return 0;
+
  }
+
  close(fd);
+
  dbp->lastpos = 2048; /* cdb_seqinit() */
+
  return 1;
+
}
+

+
static void
+
__nss_cdb_doendent(struct nss_cdb *dbp) {
+
  cdb_free(&dbp->cdb);
+
  dbp->lastpos = 0;
+
  dbp->keepopen = 0;
+
}
+

+
enum nss_status internal_function
+
__nss_cdb_setent(struct nss_cdb *dbp, int stayopen) {
+
  enum nss_status r;
+
  lock_lock(lock);
+
  if (isopen(dbp) || __nss_cdb_dosetent(dbp))
+
    r = NSS_STATUS_SUCCESS, dbp->keepopen |= stayopen;
+
  else
+
    r = NSS_STATUS_UNAVAIL;
+
  lock_unlock(lock);
+
  return r;
+
}
+

+
enum nss_status internal_function
+
__nss_cdb_endent(struct nss_cdb *dbp) {
+
  lock_lock(lock);
+
  if (isopen(dbp))
+
    __nss_cdb_doendent(dbp);
+
  lock_unlock(lock);
+
  return NSS_STATUS_SUCCESS;
+
}
+

+
static enum nss_status
+
__nss_cdb_dobyname(struct nss_cdb *dbp, const char *key, unsigned len,
+
                   void *result, char *buf, size_t bufl, int *errnop) {
+
  int r;
+

+
  if ((r = cdb_find(&dbp->cdb, key, len)) < 0)
+
    return *errnop = errno, NSS_STATUS_UNAVAIL;
+
  len = cdb_datalen(&dbp->cdb);
+
  if (!r || len < 2)
+
    return *errnop = ENOENT, NSS_STATUS_NOTFOUND;
+
  if (len >= bufl)
+
    return *errnop = ERANGE, NSS_STATUS_TRYAGAIN;
+
  if (cdb_read(&dbp->cdb, buf, len, cdb_datapos(&dbp->cdb)) != 0)
+
    return *errnop = errno, NSS_STATUS_UNAVAIL;
+
  buf[len] = '\0';
+
  if ((r = dbp->parsefn(result, buf, bufl)) < 0)
+
    return *errnop = ENOENT, NSS_STATUS_NOTFOUND;
+
  if (!r)
+
    return *errnop = ERANGE, NSS_STATUS_TRYAGAIN;
+

+
  return NSS_STATUS_SUCCESS;
+
}
+

+
enum nss_status internal_function
+
__nss_cdb_byname(struct nss_cdb *dbp, const char *name,
+
                 void *result, char *buf, size_t bufl, int *errnop) {
+
  enum nss_status r;
+
  if (*name == ':')
+
    return *errnop = ENOENT, NSS_STATUS_NOTFOUND;
+
  lock_lock(lock);
+
  if (!isopen(dbp) && !__nss_cdb_dosetent(dbp))
+
    *errnop = errno, r = NSS_STATUS_UNAVAIL;
+
  else {
+
    r = __nss_cdb_dobyname(dbp, name, strlen(name), result, buf, bufl, errnop);
+
    if (!dbp->keepopen)
+
      __nss_cdb_doendent(dbp);
+
  }
+
  lock_unlock(lock);
+
  return r;
+
}
+

+
static enum nss_status
+
__nss_cdb_dobyid(struct nss_cdb *dbp, unsigned long id,
+
                 void *result, char *buf, size_t bufl, int *errnop) {
+
  int r;
+
  unsigned len;
+
  const char *data;
+

+
  if ((r = cdb_find(&dbp->cdb, buf, sprintf(buf, ":%lu", id))) < 0)
+
    return *errnop = errno, NSS_STATUS_UNAVAIL;
+
  len = cdb_datalen(&dbp->cdb);
+
  if (!r || len < 2)
+
    return *errnop = ENOENT, NSS_STATUS_NOTFOUND;
+
  if (!(data = (const char*)cdb_get(&dbp->cdb, len, cdb_datapos(&dbp->cdb))))
+
    return *errnop = errno, NSS_STATUS_UNAVAIL;
+

+
  return __nss_cdb_dobyname(dbp, data, len, result, buf, bufl, errnop);
+
}
+

+
enum nss_status internal_function
+
__nss_cdb_byid(struct nss_cdb *dbp, unsigned long id,
+
               void *result, char *buf, size_t bufl, int *errnop) {
+
  enum nss_status r;
+
  if (bufl < 30)
+
    return *errnop = ERANGE, NSS_STATUS_TRYAGAIN;
+
  lock_lock(lock);
+
  if (!isopen(dbp) && !__nss_cdb_dosetent(dbp))
+
    *errnop = errno, r = NSS_STATUS_UNAVAIL;
+
  else {
+
    r = __nss_cdb_dobyid(dbp, id, result, buf, bufl, errnop);
+
    if (!dbp->keepopen)
+
      __nss_cdb_doendent(dbp);
+
  }
+
  lock_unlock(lock);
+
  return r;
+
}
+

+
static enum nss_status
+
__nss_cdb_dogetent(struct nss_cdb *dbp,
+
                   void *result, char *buf, size_t bufl, int *errnop) {
+
  int r;
+
  unsigned lastpos;
+

+
  if (!isopen(dbp) && !__nss_cdb_dosetent(dbp))
+
    return *errnop = errno, NSS_STATUS_UNAVAIL;
+

+
  while((lastpos = dbp->lastpos, r = cdb_seqnext(&dbp->lastpos, &dbp->cdb)) > 0)
+
  {
+
    if (cdb_keylen(&dbp->cdb) < 2) continue;
+
    if (((const char *)cdb_getkey(&dbp->cdb))[0] == ':') /* can't fail */
+
      continue;
+
    if (cdb_datalen(&dbp->cdb) >= bufl)
+
      return dbp->lastpos = lastpos, *errnop = ERANGE, NSS_STATUS_TRYAGAIN;
+
    cdb_readdata(&dbp->cdb, buf);
+
    buf[cdb_datalen(&dbp->cdb)] = '\0';
+
    if ((r = dbp->parsefn(result, buf, bufl)) == 0)
+
      return dbp->lastpos = lastpos, *errnop = ERANGE, NSS_STATUS_TRYAGAIN;
+
    if (r > 0)
+
      return NSS_STATUS_SUCCESS;
+
  }
+
  if (r < 0)
+
    return *errnop = errno, NSS_STATUS_UNAVAIL;
+
  else
+
    return *errnop = ENOENT, NSS_STATUS_NOTFOUND;
+
}
+

+
enum nss_status internal_function
+
__nss_cdb_getent(struct nss_cdb *dbp,
+
                 void *result, char *buf, size_t bufl, int *errnop) {
+
  enum nss_status r;
+
  if (bufl < 30)
+
    return *errnop = ERANGE, NSS_STATUS_TRYAGAIN;
+
  lock_lock(lock);
+
  dbp->keepopen |= 1;
+
  r = __nss_cdb_dogetent(dbp, result, buf, bufl, errnop);
+
  lock_unlock(lock);
+
  return r;
+
}
+

added external/tinycdb/nss_cdb.h
@@ -0,0 +1,101 @@
+
/* $Id: nss_cdb.h,v 1.2 2003-11-03 20:10:46 mjt Exp $
+
 * nss_cdb common include file.
+
 *
+
 * This file is a part of tinycdb package by Michael Tokarev, mjt@corpit.ru.
+
 * Public domain.
+
 */
+

+

+
#include <sys/types.h>
+
#include <stdlib.h>
+
#include <nss.h>
+
#include "cdb.h"
+

+
#ifndef NSSCDB_DIR
+
# define NSSCDB_DIR "/etc"
+
#endif
+
#ifndef NSSCDB_DB
+
# define NSSCDB_DB(name) NSSCDB_DIR "/" name ".cdb"
+
#endif
+

+
typedef int (nss_parse_fn)(void *result, char *buf, size_t bufl);
+

+
struct nss_cdb {
+
  nss_parse_fn *parsefn;
+
  const char *dbname;
+
  int keepopen;
+
  unsigned lastpos;
+
  struct cdb cdb;
+
};
+

+
enum nss_status
+
__nss_cdb_setent(struct nss_cdb *dbp, int stayopen);
+
enum nss_status
+
__nss_cdb_endent(struct nss_cdb *dbp);
+
enum nss_status
+
__nss_cdb_getent(struct nss_cdb *dbp,
+
		 void *result, char *buf, size_t bufl, int *errnop);
+
enum nss_status
+
__nss_cdb_byname(struct nss_cdb *dbp, const char *name,
+
		 void *result, char *buf, size_t bufl, int *errnop);
+
enum nss_status
+
__nss_cdb_byid(struct nss_cdb *dbp, unsigned long id,
+
	       void *result, char *buf, size_t bufl, int *errnop);
+

+
#define nss_common(dbname,structname,entname) \
+
static int \
+
nss_##dbname##_parse(structname *result, char *buf, size_t bufl); \
+
static struct nss_cdb db = { \
+
  (nss_parse_fn*)&nss_##dbname##_parse, \
+
  NSSCDB_DB(#dbname),0,0,CDB_STATIC_INIT}; \
+
enum nss_status _nss_cdb_set##entname(int stayopen) { \
+
  return __nss_cdb_setent(&db, stayopen); \
+
} \
+
enum nss_status _nss_cdb_end##entname(void) { \
+
  return __nss_cdb_endent(&db); \
+
} \
+
enum nss_status \
+
_nss_cdb_get##entname##_r(structname *result, \
+
                           char *buf, size_t bufl, int *errnop) { \
+
  return __nss_cdb_getent(&db, result, buf, bufl, errnop); \
+
}
+

+
#define nss_getbyname(getbyname, structname) \
+
enum nss_status \
+
_nss_cdb_##getbyname##_r(const char *name, structname *result, \
+
		          char *buf, size_t bufl, int *errnop) { \
+
  return __nss_cdb_byname(&db, name, result, buf, bufl, errnop); \
+
}
+

+
#define nss_getbyid(getbyid, structname, idtype) \
+
enum nss_status \
+
_nss_cdb_##getbyid##_r(idtype id, structname *result, \
+
		           char *buf, size_t bufl, int *errnop) { \
+
  return __nss_cdb_byid(&db, id, result, buf, bufl, errnop); \
+
}
+

+
#define STRING_FIELD(line, variable) \
+
   variable = line; \
+
   while(*line != ':') \
+
     if (!*line++) return -1; \
+
   *line++ = '\0'
+

+
#define INT_FIELD(line, variable, convert) \
+
 { \
+
   char *endp; \
+
   variable = convert(strtoul(line, &endp, 10)); \
+
   if (endp == line) return -1; \
+
   if (*endp++ != ':') return -1; \
+
   line = endp; \
+
 }
+

+
#define INT_FIELD_MAYBE_NULL(line, variable, convert, default) \
+
 { \
+
   char *endp; \
+
   if (!*line) return -1; \
+
   variable = convert(strtoul(line, &endp, 10)); \
+
   if (*endp != ':') return -1; \
+
   if (endp == line) variable = convert(default); \
+
   line = endp; \
+
 }
+

added external/tinycdb/nss_cdb.map
@@ -0,0 +1,21 @@
+
# $Id: nss_cdb.map,v 1.1 2006-06-28 13:25:46 mjt Exp $
+
# libnss_cdb symbol map file for GNU LD
+
{
+
  global:
+
   _nss_cdb_endpwent;
+
   _nss_cdb_getpwnam_r;
+
   _nss_cdb_endspent;
+
   _nss_cdb_getspent_r;
+
   _nss_cdb_getpwent_r;
+
   _nss_cdb_getgrgid_r;
+
   _nss_cdb_endgrent;
+
   _nss_cdb_getgrent_r;
+
   _nss_cdb_setspent;
+
   _nss_cdb_setpwent;
+
   _nss_cdb_getpwuid_r;
+
   _nss_cdb_getspnam_r;
+
   _nss_cdb_setgrent;
+
   _nss_cdb_getgrnam_r;
+
  local:
+
    *;
+
};
added external/tinycdb/tests.ok
@@ -0,0 +1,83 @@
+
Create simple db
+
0
+
checksum may fail if no md5sum program
+
97549c2e76e2d446430a392d77ed1bcb
+
Dump simple db
+
+3,4:one->here
+
+1,1:a->b
+
+1,3:b->abc
+
+3,4:one->also
+

+
0
+
Stats for simple db
+
number of records: 4
+
key min/avg/max length: 1/2/3
+
val min/avg/max length: 1/3/4
+
hash tables/entries/collisions: 3/8/1
+
hash table min/avg/max length: 2/3/4
+
hash table distances:
+
 d0:      3 75%
+
 d1:      1 25%
+
 d2:      0  0%
+
 d3:      0  0%
+
 d4:      0  0%
+
 d5:      0  0%
+
 d6:      0  0%
+
 d7:      0  0%
+
 d8:      0  0%
+
 d9:      0  0%
+
 >9:      0  0%
+
0
+
Query simple db (two records match)
+
herealso
+
0
+
Query for non-existed key
+
100
+
Doing 600 repeated records
+
0
+
checksum may fail if no md5sum program
+
412a0b7578efca528bf8398c8811caf4
+
cdb stats should show 601 record
+
number of records: 601
+
key min/avg/max length: 1/1/1
+
val min/avg/max length: 3/3/5
+
hash tables/entries/collisions: 2/1202/599
+
hash table min/avg/max length: 2/601/1200
+
hash table distances:
+
 d0:      2  0%
+
 d1:      1  0%
+
 d2:      1  0%
+
 d3:      1  0%
+
 d4:      1  0%
+
 d5:      1  0%
+
 d6:      1  0%
+
 d7:      1  0%
+
 d8:      1  0%
+
 d9:      1  0%
+
 >9:    590 98%
+
0
+
Querying key
+
other
+
0
+
Dumping and re-creating db
+
0
+
0
+
Handling large key size
+
cdb: (stdin): bad format
+
2
+
Handling large value size
+
cdb: (stdin): bad format
+
2
+
Handling invalid input format (short file)
+
cdb: unable to read: short file
+
2
+
Creating db with eol in key and value
+
0
+
checksum may fail if no md5sum program
+
1d444fe759c26d36f500d01c41cfda40
+
Querying key-value with eol
+
b
+
0
+
Handling file size limits
+
cdb: cdb_make_put: File too large
+
111
added external/tinycdb/tests.sh
@@ -0,0 +1,144 @@
+
#! /bin/sh
+

+
# $Id: tests.sh,v 1.3 2003-11-03 21:08:55 mjt Exp $
+
# This script will run tests for cdb.
+
# Execute with ./tests.sh ./cdb
+
# (first arg if present gives path to cdb tool to use, default is `cdb').
+
#
+
# This file is a part of tinycdb package by Michael Tokarev, mjt@corpit.ru.
+
# Public domain.
+

+
case "$1" in
+
  "") cdb=cdb ;;
+
  *) cdb="$1" ;;
+
esac
+

+
do_csum() {
+
  echo checksum may fail if no md5sum program
+
  md5sum $1 | sed -e 's|[ 	].*||' -e 'y|[ABCDEF]|[abcdef]|'
+
}
+

+
rm -f 1.cdb 1a.cdb
+

+
echo Create simple db
+
echo "+3,4:one->here
+
+1,1:a->b
+
+1,3:b->abc
+
+3,4:one->also
+

+
" | $cdb -c 1.cdb
+
echo $?
+
do_csum 1.cdb
+

+
echo Dump simple db
+
$cdb -d 1.cdb
+
echo $?
+

+
echo Stats for simple db
+
$cdb -s 1.cdb
+
echo $?
+

+
echo "Query simple db (two records match)"
+
$cdb -q 1.cdb one
+
echo "
+
$?"
+

+
echo Query for non-existed key
+
$cdb -q 1.cdb none
+
echo $?
+

+
echo Doing 600 repeated records
+
(
+
 for i in 0 1 2 3 4 5 ; do
+
  for j in 0 1 2 3 4 5 6 7 8 9 ; do
+
   for k in 0 1 2 3 4 5 6 7 8 9 ; do
+
    echo "+1,3:a->$i$j$k"
+
   done
+
  done
+
 done
+
 echo "+1,5:b->other"
+
 echo
+
) | $cdb -c 1.cdb
+
echo $?
+
do_csum 1.cdb
+
echo cdb stats should show 601 record
+
$cdb -s 1.cdb
+
echo $?
+

+
echo Querying key
+
$cdb -q 1.cdb b
+
echo "
+
"$?
+

+
echo Dumping and re-creating db
+
$cdb -d 1.cdb | $cdb -c 1a.cdb
+
echo $?
+
cmp 1.cdb 1a.cdb
+

+
$cdb -d -m 1.cdb | $cdb -c -m 1a.cdb
+
echo $?
+
cmp 1.cdb 1a.cdb
+

+
echo Handling large key size
+
echo "+123456789012,1:" | $cdb -c 1.cdb
+
echo $?
+

+
echo Handling large value size
+
echo "+1,123456789012:" | $cdb -c 1.cdb
+
echo $?
+

+
echo "Handling invalid input format (short file)"
+
echo "+10,10:" | $cdb -c 1.cdb
+
echo $?
+

+
echo Creating db with eol in key and value
+
echo "+2,2:a
+
->b
+

+
" | $cdb -c 1.cdb
+
echo $?
+
do_csum 1.cdb
+

+
echo Querying key-value with eol
+
$cdb -q 1.cdb "a
+
"
+
echo $?
+

+
echo Handling file size limits
+
(
+
 ulimit -f 3
+
 trap '' 25
+
 (
+
  for i in 0 1 2 3 4 5 6 7 8 9 ; do
+
   for j in 0 1 2 3 4 5 6 7 8 9 ; do
+
    for k in 0 1 2 3 4 5 6 7 8 9 ; do
+
     echo "+4,4:k$i$j$k->v$i$j$k"
+
    done
+
   done
+
  done
+
  echo
+
 ) | $cdb -c 1.cdb
+
 echo $?
+
)
+

+
if false ; then # does not work for now, bugs in libc
+
echo Handling oom condition
+
(
+
 for i0 in 0 1 2 3 4 5 6 7 8 9 ; do
+
  for i1 in 0 1 2 3 4 5 6 7 8 9 ; do
+
   for i2 in 0 1 2 3 4 5 6 7 8 9 ; do
+
    for i3 in 0 1 2 3 4 5 6 7 8 9 ; do
+
     for i4 in 0 1 2 3 4 5 6 7 8 9 ; do
+
      echo "+5,0:$i0$i1$i2$i3$i4->"
+
     done
+
    done
+
   done
+
  done
+
 done
+
 echo
+
) | (ulimit -v 1900; $cdb -c 1.cdb)
+
echo $?
+
fi
+

+
rm -rf 1.cdb 1a.cdb 1.cdb.tmp
+
exit 0
added external/tinycdb/tinycdb.spec
@@ -0,0 +1,89 @@
+
# $Id: tinycdb.spec,v 1.6 2009-01-31 17:12:22 mjt Exp $
+
# tinycdb RPM spec file.
+
#
+
# This file is a part of tinycdb package by Michael Tokarev, mjt@corpit.ru.
+
# Public domain.
+

+
Summary: A package for maintenance of constant databases
+
Name: tinycdb
+
Version: 0.77
+
Release: 1
+
Source: ftp://ftp.corpit.ru/pub/tinycdb/tinycdb_%version.tar.gz
+
License: Public Domain
+
Group: System Environment/Libraries
+
Prefix: %{_prefix}
+
BuildRoot: %{_tmppath}/%{name}-root
+
Summary: TinyCDB - a Constant DataBase
+

+
%description
+
tinycdb is a small, fast and reliable utility set and subroutine
+
library for creating and reading constant databases. The database
+
structure is tuned for fast reading:
+
+ Successful lookups take normally just two disk accesses.
+
+ Unsuccessful lookups take only one disk access.
+
+ Small disk space and memory size requirements; a database
+
  uses 2048 bytes for the header and 24 bytes plus size of
+
  (key,value) per record.
+
+ Maximum database size is 4GB; individual record size is not
+
  otherwise limited.
+
+ Portable file format.
+
+ Fast creation of new databases.
+
+ No locking, updates are atomical.
+

+
This package contains both the utility and the development
+
files, together with nss_cdb module.
+

+
%package devel
+
Summary: Development files for the tinycdb library.
+
Group: System Environment/Libraries
+
Requires: %name = %version-%release
+
Summary: Development files for tinycdb
+
%description devel
+
tinycdb is a small, fast and reliable utility set and subroutine
+
library for creating and reading constant databases.
+

+
This package contains tinycdb development libraries and header files.
+

+
%prep
+
%setup -q
+

+
%build
+
make CFLAGS="$RPM_OPT_FLAGS" \
+
 staticlib sharedlib cdb-shared nss \
+
 sysconfdir=/etc
+

+
%install
+
rm -rf $RPM_BUILD_ROOT
+
mkdir -p $RPM_BUILD_ROOT
+
%makeinstall DESTDIR=$RPM_BUILD_ROOT \
+
 libdir=%_libdir bindir=%_bindir mandir=%_mandir \
+
 syslibdir=/%_lib sysconfdir=/etc \
+
 includedir=%_includedir \
+
 install-all install-nss install-piclib install-sharedlib \
+
 INSTALLPROG=cdb-shared CP="cp -p"
+

+
%files
+
%defattr(-,root,root)
+
%_bindir/*
+
%_mandir/man1/*
+
%_mandir/man5/*
+
%_libdir/libcdb.so.*
+
/%_lib/libnss_cdb*
+
/etc/cdb-Makefile
+
%doc ChangeLog NEWS debian/changelog
+

+
%files devel
+
%defattr(-,root,root)
+
%_libdir/libcdb.a
+
%_libdir/libcdb_pic.a
+
%_libdir/libcdb.so
+
%_mandir/man3/*
+
%_includedir/*
+

+
%clean
+
rm -rf $RPM_BUILD_ROOT
+

+
%post -p /sbin/ldconfig
+
%postun -p /sbin/ldconfig
+

+
%changelog
added libpkg/Makefile
@@ -0,0 +1,22 @@
+
.include <bsd.own.mk>
+

+
LIB=	pkg
+
INCS=	pkg.h pkgdb.h
+
WARNS=	6
+
SHLIBDIR?=	/usr/lib
+
SHLIB_MAJOR=	0
+

+
SRCS=	pkgdb.c pkgdb_cache.c pkg_compat.c
+

+
CFLAGS+=  -std=c99
+
CFLAGS+=  -I${.CURDIR} -I${.CURDIR}/../external/tinycdb -I${.CURDIR}/../external/cjson/
+
LDADD+=   -L${.CURDIR}/../external/tinycdb -L${.CURDIR}/../external/cjson -lcdb -lcjson -lm
+

+
DEBUG_FLAGS+=  -g
+
.if defined(PROFILE_BUILD)
+
DEBUG_FLAGS+=    -pg
+
.endif
+
NO_MAN=   yes
+

+
.include <bsd.lib.mk>
+

added libpkg/pkg.h
@@ -0,0 +1,11 @@
+
#ifndef _PKG_H
+
#define _PKG_H
+
struct pkg {
+
	const char *name;
+
	const char *version;
+
	const char *origin;
+
	const char *comments;
+
	const char *desc;
+
};
+

+
#endif
added libpkg/pkg_compat.c
@@ -0,0 +1,214 @@
+
#include <ctype.h>
+
#include <err.h>
+
#include <stdlib.h>
+
#include <string.h>
+

+
#include "pkg_compat.h"
+

+
static void
+
str_lowercase(char *str)
+
{
+
	while (*str) {
+
		*str = tolower(*str);
+
		++str;
+
	}
+
}
+

+

+
static struct plist *
+
pkg_compat_new_plist_entry(void)
+
{
+
	struct plist *ret;
+

+
	ret = (struct plist *)malloc(sizeof(struct plist));
+
	bzero(ret, sizeof(struct plist));
+
	return ret;
+
}
+

+
static void
+
pkg_compat_delete_plist(struct oldpackage *pkg, bool all, enum plist_t type, const char *name)
+
{
+
	struct plist *p = pkg->head;
+

+
	while (p) {
+
		struct plist *pnext = p->next;
+

+
		if (p->type == type && (!name || !strcmp(name, p->name))) {
+
			free(p->name);
+
			if (p->prev)
+
				p->prev->next = pnext;
+
			else
+
				pkg->head = pnext;
+
			if (pnext)
+
				pnext->prev = p->prev;
+
			else
+
				pkg->tail = p->prev;
+
			free(p);
+
			if (!all)
+
				return;
+
			p = pnext;
+
		}
+
		else
+
			p = p->next;
+
	}
+
}
+

+
static void
+
pkg_compat_free_plist(struct oldpackage *pkg)
+
{
+
	struct plist *p = pkg->head;
+

+
	while (p) {
+
		struct plist *p1 = p->next;
+

+
		free(p->name);
+
		free(p);
+
		p = p1;
+
	}
+
	pkg->head = pkg->tail = NULL;
+
}
+

+
static int
+
pkg_compat_plist_cmd(const char *s, char **arg)
+
{
+
	char cmd[FILENAME_MAX + 20];    /* 20 == fudge for max cmd len */
+
	char *cp;
+
	const char *sp;
+

+
	strlcpy(cmd, s, sizeof(cmd));
+
	str_lowercase(cmd);
+
	cp = cmd;
+
	sp = s;
+
	while (*cp) {
+
		if (isspace(*cp)) {
+
			*cp = '\0';
+
			while (isspace(*sp)) /* Never sure if macro, increment later */
+
				++sp;
+
			break;
+
		}
+
		++cp, ++sp;
+
	}
+
	if (arg)
+
		*arg = (char *)sp;
+
	if (!strcmp(cmd, "cwd"))
+
		return PLIST_CWD;
+
	else if (!strcmp(cmd, "srcdir"))
+
		return PLIST_SRC;
+
	else if (!strcmp(cmd, "cd"))
+
		return PLIST_CWD;
+
	else if (!strcmp(cmd, "exec"))
+
		return PLIST_CMD;
+
	else if (!strcmp(cmd, "unexec"))
+
		return PLIST_UNEXEC;
+
	else if (!strcmp(cmd, "mode"))
+
		return PLIST_CHMOD;
+
	else if (!strcmp(cmd, "owner"))
+
		return PLIST_CHOWN;
+
	else if (!strcmp(cmd, "group"))
+
		return PLIST_CHGRP;
+
	else if (!strcmp(cmd, "noinst"))
+
		return PLIST_NOINST;
+
	else if (!strcmp(cmd, "comment")) {
+
		if (!strncmp(*arg, "ORIGIN:", 7)) {
+
			*arg += 7;
+
			return PLIST_ORIGIN;
+
		} else if (!strncmp(*arg, "DEPORIGIN:", 10)) {
+
			*arg += 10;
+
			return PLIST_DEPORIGIN;
+
		}
+
		return PLIST_COMMENT;
+
	} else if (!strcmp(cmd, "ignore"))
+
		return PLIST_IGNORE;
+
	else if (!strcmp(cmd, "ignore_inst"))
+
		return PLIST_IGNORE_INST;
+
	else if (!strcmp(cmd, "name"))
+
		return PLIST_NAME;
+
	else if (!strcmp(cmd, "display"))
+
		return PLIST_DISPLAY;
+
	else if (!strcmp(cmd, "pkgdep"))
+
		return PLIST_PKGDEP;
+
	else if (!strcmp(cmd, "conflicts"))
+
		return PLIST_CONFLICTS;
+
	else if (!strcmp(cmd, "mtree"))
+
		return PLIST_MTREE;
+
	else if (!strcmp(cmd, "dirrm"))
+
		return PLIST_DIR_RM;
+
	else if (!strcmp(cmd, "option"))
+
		return PLIST_OPTION;
+
	else
+
		return -1;
+
}
+

+
static void
+
pkg_compat_add_plist(cJSON *p, enum plist_t type, const char *arg)
+
{
+
	char *tmp;
+

+
	switch (type) {
+
		case PLIST_NAME:
+
			tmp = strrchr(arg, '-');
+
			tmp[0] = '\0';
+
			tmp++;
+
			cJSON_AddStringToObject(p, "name", arg);
+
			cJSON_AddStringToObject(p, "version", tmp);
+
			break;
+

+
		case PLIST_ORIGIN:
+
			cJSON_AddStringToObject(p, "origin", arg);
+
			break;
+
		
+
		case PLIST_COMMENT:
+
			cJSON_AddStringToObject(p, "comment", arg);
+
			break;
+

+
		default:
+
			break;
+
	}
+
}
+

+
static void
+
pkg_compat_read_plist(cJSON *pkg, char *plist_str)
+
{
+
	int cmd;
+
	char *buf, *next, *cp;
+

+
	buf = plist_str;
+
	while ((next = strchr(buf, '\n')) != NULL) {
+
		next[0] = '\0';
+

+
		while (strlen(buf) > 0 && isspace(buf[strlen(buf) - 1]))
+
			buf[strlen(buf) - 1] = '\0';
+

+
		if (buf[0] != '@') {
+
			cmd = PLIST_FILE;
+
			goto bottom;
+
		}
+

+
		cmd = pkg_compat_plist_cmd(buf + 1, &cp);
+
		if (cmd == -1) {
+
			warnx("%s: unknown command '%s'",
+
					__func__, buf);
+
			goto bottom;
+
		}
+
		if (*cp == '\0') {
+
			cp = NULL;
+
			if (cmd == PLIST_PKGDEP) {
+
				warnx("corrupted record (pkgdep line without argument), ignoring");
+
				cmd = -1;
+
			}
+
			goto bottom;
+
		}
+
bottom:
+
		pkg_compat_add_plist(pkg, cmd, cp);
+
		buf = next;
+
		buf++;
+
	}
+
}
+

+
cJSON *
+
pkg_compat_converter(char *plist_str)
+
{
+
	cJSON *rootpkg = cJSON_CreateObject();
+
	pkg_compat_read_plist(rootpkg, plist_str);
+
	return rootpkg;
+
}
added libpkg/pkg_compat.h
@@ -0,0 +1,35 @@
+
#ifndef _PKG_COMPAT_H
+
#define _PKG_COMPAT_H
+

+
#include <stdbool.h>
+
#include <stdio.h>
+
#include <cJSON.h>
+

+
enum plist_t {
+
	PLIST_FILE, PLIST_CWD, PLIST_CMD, PLIST_CHMOD,
+
	PLIST_CHOWN, PLIST_CHGRP, PLIST_COMMENT, PLIST_IGNORE,
+
	PLIST_NAME, PLIST_UNEXEC, PLIST_SRC, PLIST_DISPLAY,
+
	PLIST_PKGDEP, PLIST_CONFLICTS, PLIST_MTREE, PLIST_DIR_RM,
+
	PLIST_IGNORE_INST, PLIST_OPTION, PLIST_ORIGIN, PLIST_DEPORIGIN,
+
	PLIST_NOINST
+
};
+

+

+
struct plist {
+
	struct plist *prev, *next;
+
	char *name;
+
	bool marked;
+
	enum plist_t type;
+
};
+

+
struct oldpackage {
+
	struct plist *head, *tail;
+
	const char *name;
+
	const char *origin;
+

+

+
};
+

+
cJSON *pkg_compat_converter(char *);
+

+
#endif
added libpkg/pkgdb.c
@@ -0,0 +1,12 @@
+
#include "pkgdb.h"
+
#include "pkgdb_cache.h"
+

+
struct pkg **
+
pkgdb_list_packages() {
+
	/* first check if the cache has to be rebuild */
+
	struct pkg **pkgs;
+
	pkgdb_cache_update();
+
	pkgs = pkgdb_cache_list_packages();
+
	
+
	return (pkgs);
+
}
added libpkg/pkgdb.h
@@ -0,0 +1,10 @@
+
#ifndef _PKGDB_H
+
#define _PKGDB_H
+
#include <pkg.h>
+

+
#define PKG_DBDIR "/var/db/pkg"
+

+
struct pkg **pkgdb_list_packages(void);
+

+

+
#endif
added libpkg/pkgdb_cache.c
@@ -0,0 +1,143 @@
+
#include <dirent.h>
+
#include <err.h>
+
#include <errno.h>
+
#include <stdlib.h>
+
#include <stdio.h>
+
#include <string.h>
+
#include <sys/param.h>
+
#include <sys/types.h>
+
#include <sys/stat.h>
+
#include <unistd.h>
+

+
#include <cdb.h>
+
#include <cJSON.h>
+

+
#include "pkg_compat.h"
+
#include "pkgdb_cache.h"
+

+
static void pkgdb_cache_rebuild(const char *, const char *);
+

+
static void
+
pkgdb_cache_rebuild(const char *pkg_dbdir, const char *cache_path)
+
{
+
	int fd;
+
	char key[BUFSIZ];
+
	char *value;
+
	char tmppath[MAXPATHLEN];
+
	char contentpath[MAXPATHLEN];
+
	struct cdb_make cdb_make;
+
	DIR *dir;
+
	struct dirent *portsdir;
+
	struct stat st;
+
	FILE *content;
+
	char *content_buffer;
+
	cJSON *manifest_json;
+
	int nb_packages = 0;
+

+

+
	strlcpy(tmppath, pkg_dbdir, MAXPATHLEN);
+
	strlcat(tmppath, "/pkgdb.cache", MAXPATHLEN);
+

+
	printf("Rebuilding cache...\n");
+
	fd = mkstemp(tmppath);
+

+
	cdb_make_start(&cdb_make, fd);
+

+
	/* Now go through pkg_dbdir rebuild the cache */
+

+
	if ((dir = opendir(pkg_dbdir)) != NULL) {
+
		while ((portsdir = readdir(dir)) != NULL) {
+
			if (strcmp(portsdir->d_name, ".") != 0 &&
+
					strcmp(portsdir->d_name, "..") !=0) {
+
				strlcpy(contentpath, pkg_dbdir, MAXPATHLEN);
+
				strlcat(contentpath, "/", MAXPATHLEN);
+
				strlcat(contentpath, portsdir->d_name, MAXPATHLEN);
+
				strlcat(contentpath, "/+CONTENTS", MAXPATHLEN);
+

+
				if (stat(contentpath, &st) == -1) {
+
					warn("Unable to read %s informations, skipping:", portsdir->d_name);
+
					continue;
+
				}
+

+
				if ((content = fopen(contentpath, "r")) == NULL) {
+
					warn("Unable to read %s file, skipping", contentpath);
+
					continue;
+
				}
+

+
				content_buffer = malloc(st.st_size + 1);
+
				fread(content_buffer, st.st_size, 1, content);
+
				fclose(content);
+

+
				manifest_json = cJSON_Parse(content_buffer);
+
				if (manifest_json == 0)
+
					manifest_json = pkg_compat_converter(content_buffer);
+

+
				if (manifest_json == 0)
+
					continue; /* skipping */
+

+
				nb_packages++;
+
				snprintf(key, BUFSIZ, "%d_name",nb_packages);
+
				value = cJSON_GetObjectItem(manifest_json, "name")->valuestring;
+
				cdb_make_add(&cdb_make, key, strlen(key), value, strlen(value));
+
				snprintf(key, BUFSIZ, "%d_version", nb_packages);
+
				value = cJSON_GetObjectItem(manifest_json, "version")->valuestring;
+
				cdb_make_add(&cdb_make, key, strlen(key), value, strlen(value));
+
				cJSON_Delete(manifest_json);
+
			}
+
		}
+
	}
+

+
	cdb_make_finish(&cdb_make);
+

+
	close(fd);
+
	rename(tmppath, cache_path);
+
	chmod(cache_path, 0644);
+
}
+

+
void
+
pkgdb_cache_update()
+
{
+
	const char *pkg_dbdir;
+
	char cache_path[MAXPATHLEN];
+
	struct stat dir_st, cache_st;
+
	uid_t uid;
+

+
	if ((pkg_dbdir = getenv("PKG_DBDIR")) == NULL)
+
		pkg_dbdir = PKG_DBDIR;
+

+
	uid = getuid();
+

+
	if (stat(pkg_dbdir, &dir_st) == -1) {
+
		if (uid != 0)
+
			err(EXIT_FAILURE, "%s:", pkg_dbdir);
+

+
		if (errno == ENOENT)
+
			return;
+
		else
+
			err(EXIT_FAILURE, "%s:", pkg_dbdir);
+
	}
+

+
	strlcpy(cache_path, pkg_dbdir, MAXPATHLEN);
+
	strlcat(cache_path, "/pkgdb.cache", MAXPATHLEN);
+

+
	if (stat(cache_path, &cache_st) == -1) {
+
		if (errno == ENOENT) {
+
			if (uid == 0)
+
				pkgdb_cache_rebuild(pkg_dbdir, cache_path);
+
			return;
+
		} else {
+
			err(EXIT_FAILURE, "%s:", cache_path);
+
		}
+
	}
+

+
	if ( dir_st.st_mtime > cache_st.st_mtime )
+
		if (uid == 0)
+
			pkgdb_cache_rebuild(pkg_dbdir, cache_path);
+
}
+

+
struct pkg **
+
pkgdb_cache_list_packages()
+
{
+
	/* TODO */
+
	return NULL;
+
}
added libpkg/pkgdb_cache.h
@@ -0,0 +1,8 @@
+
#ifndef _PKGDB_CACHE_H
+
#define _PKGDB_CACHE_H
+
#include "pkgdb.h"
+

+
void pkgdb_cache_update(void);
+
struct pkg **pkgdb_cache_list_packages(void);
+

+
#endif
modified pkg/Makefile
@@ -2,8 +2,8 @@ PROG= pkg
SRCS=	main.c \
		info.c

-
#CFLAGS+=	-I${.CURDIR}/../libpkg -I${.CURDIR}/../external
-
#LDADD+=	-L${.CURDIR}/../external -L../libpkg -lpkg -lcjson
+
CFLAGS+=	-I${.CURDIR}/../libpkg -I${.CURDIR}/../external
+
LDADD+=	-L${.CURDIR}/../external -L../libpkg -lpkg
NO_MAN= true

.include <bsd.prog.mk>
modified pkg/info.c
@@ -1,9 +1,18 @@
-
#include "info.h"
#include <err.h>
#include <stdio.h>
+
#include <pkg.h>
+
#include <pkgdb.h>
+

+
#include "info.h"

void *
cmd_info(int argc, char **argv)
{
-
	printf("info\n");
+
	struct pkg **pkgs = NULL;
+

+
	if (argc == 1) {
+
		pkgs = pkgdb_list_packages();
+
	} else {
+
		printf("Not implemented yet\n");
+
	}
}