Blame src/keyvalue.cc

Packit 8f70b4
/*
Packit 8f70b4
 * lftp - file transfer program
Packit 8f70b4
 *
Packit 8f70b4
 * Copyright (c) 1996-2012 by Alexander V. Lukyanov (lav@yars.free.net)
Packit 8f70b4
 *
Packit 8f70b4
 * This program is free software; you can redistribute it and/or modify
Packit 8f70b4
 * it under the terms of the GNU General Public License as published by
Packit 8f70b4
 * the Free Software Foundation; either version 3 of the License, or
Packit 8f70b4
 * (at your option) any later version.
Packit 8f70b4
 *
Packit 8f70b4
 * This program is distributed in the hope that it will be useful,
Packit 8f70b4
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 8f70b4
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 8f70b4
 * GNU General Public License for more details.
Packit 8f70b4
 *
Packit 8f70b4
 * You should have received a copy of the GNU General Public License
Packit 8f70b4
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
Packit 8f70b4
 */
Packit 8f70b4
Packit 8f70b4
#include <config.h>
Packit 8f70b4
#include "keyvalue.h"
Packit 8f70b4
#include <unistd.h>
Packit 8f70b4
#include "trio.h"
Packit 8f70b4
#include <fcntl.h>
Packit 8f70b4
#include <errno.h>
Packit 8f70b4
Packit 8f70b4
int KeyValueDB::Read(int fd)
Packit 8f70b4
{
Packit 8f70b4
   xstring key;
Packit 8f70b4
   xstring value;
Packit 8f70b4
Packit 8f70b4
   int c;
Packit 8f70b4
Packit 8f70b4
   FILE *f=fdopen(fd,"r");
Packit 8f70b4
Packit 8f70b4
   for(;;)
Packit 8f70b4
   {
Packit 8f70b4
      c=getc(f);
Packit 8f70b4
Packit 8f70b4
      // skip leading space
Packit 8f70b4
      while(c!=EOF && (c==' ' || c=='\t'))
Packit 8f70b4
	 c=getc(f);
Packit 8f70b4
Packit 8f70b4
      if(c==EOF)
Packit 8f70b4
	 break;
Packit 8f70b4
      if(c=='\n')
Packit 8f70b4
	 continue;   // next line
Packit 8f70b4
Packit 8f70b4
      key.truncate(0);
Packit 8f70b4
      for(;;)
Packit 8f70b4
      {
Packit 8f70b4
	 key.append(c);
Packit 8f70b4
	 c=getc(f);
Packit 8f70b4
	 if(c==EOF)
Packit 8f70b4
	    break;
Packit 8f70b4
	 if(c==' ' || c=='\t' || c=='\n')
Packit 8f70b4
	    break;
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      if(c==EOF || c=='\n' || key.length()==0)
Packit 8f70b4
	 break;	// invalid line
Packit 8f70b4
Packit 8f70b4
      // skip space in mid
Packit 8f70b4
      while(c!=EOF && (c==' ' || c=='\t'))
Packit 8f70b4
	 c=getc(f);
Packit 8f70b4
Packit 8f70b4
      if(c==EOF || c=='\n')
Packit 8f70b4
	 break;
Packit 8f70b4
Packit 8f70b4
      value.truncate(0);
Packit 8f70b4
      for(;;)
Packit 8f70b4
      {
Packit 8f70b4
	 value.append(c);
Packit 8f70b4
	 c=getc(f);
Packit 8f70b4
	 if(c==EOF)
Packit 8f70b4
	    break;
Packit 8f70b4
	 if(c=='\n')
Packit 8f70b4
	    break;
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      Add(key,value);
Packit 8f70b4
Packit 8f70b4
      if(c==EOF)
Packit 8f70b4
	 break;
Packit 8f70b4
   }
Packit 8f70b4
   fclose(f);
Packit 8f70b4
   return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int KeyValueDB::VKeyCompare(const void *a,const void *b)
Packit 8f70b4
{
Packit 8f70b4
   KeyValueDB::Pair *pa=*(KeyValueDB::Pair*const*)a;
Packit 8f70b4
   KeyValueDB::Pair *pb=*(KeyValueDB::Pair*const*)b;
Packit 8f70b4
   return KeyValueDB::KeyCompare(pa,pb);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void KeyValueDB::Sort()
Packit 8f70b4
{
Packit 8f70b4
   int count=0;
Packit 8f70b4
   Pair *scan;
Packit 8f70b4
   for(scan=chain; scan; scan=scan->next)
Packit 8f70b4
      count++;
Packit 8f70b4
Packit 8f70b4
   if(count==0)
Packit 8f70b4
      return;
Packit 8f70b4
Packit 8f70b4
   Pair **arr=(Pair**)alloca(count*sizeof(*arr));
Packit 8f70b4
   count=0;
Packit 8f70b4
   for(scan=chain; scan; scan=scan->next)
Packit 8f70b4
      arr[count++]=scan;
Packit 8f70b4
Packit 8f70b4
   qsort(arr,count,sizeof(*arr),&KeyValueDB::VKeyCompare);
Packit 8f70b4
Packit 8f70b4
   chain=0;
Packit 8f70b4
   while(count--)
Packit 8f70b4
   {
Packit 8f70b4
      arr[count]->next=chain;
Packit 8f70b4
      chain=arr[count];
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
char *KeyValueDB::Format(StringMangler value_mangle)
Packit 8f70b4
{
Packit 8f70b4
   Sort();
Packit 8f70b4
Packit 8f70b4
   Pair *p;
Packit 8f70b4
   int max_key_len=0;
Packit 8f70b4
Packit 8f70b4
   for(p=chain; p; p=p->next)
Packit 8f70b4
   {
Packit 8f70b4
      int len=strlen(p->key);
Packit 8f70b4
      if(len>max_key_len)
Packit 8f70b4
	 max_key_len=len;
Packit 8f70b4
   }
Packit 8f70b4
   max_key_len&=~7;  // save some bytes
Packit 8f70b4
Packit 8f70b4
   xstring buf("");
Packit 8f70b4
   for(p=chain; p; p=p->next)
Packit 8f70b4
      buf.appendf("%-*s\t%s\n",max_key_len,p->key.get(),value_mangle(p->value));
Packit 8f70b4
   return buf.borrow();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int KeyValueDB::Write(int fd)
Packit 8f70b4
{
Packit 8f70b4
   xstring_ca buf(Format());
Packit 8f70b4
   int res=write(fd,buf,strlen(buf));
Packit 8f70b4
   close(fd);
Packit 8f70b4
   return res;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void KeyValueDB::Add(const char *key,const char *value)
Packit 8f70b4
{
Packit 8f70b4
   Pair **p=LookupPair(key);
Packit 8f70b4
   if(!p)
Packit 8f70b4
      AddPair(NewPair(key,value));
Packit 8f70b4
   else
Packit 8f70b4
      p[0]->SetValue(value);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void KeyValueDB::Remove(const char *key)
Packit 8f70b4
{
Packit 8f70b4
   Pair **p=LookupPair(key);
Packit 8f70b4
   if(p)
Packit 8f70b4
      Purge(p);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
KeyValueDB::Pair **KeyValueDB::LookupPair(const char *key) const
Packit 8f70b4
{
Packit 8f70b4
   for(const Pair * const*p=&chain; *p; p=&(*p)->next)
Packit 8f70b4
   {
Packit 8f70b4
      if((*p)->KeyCompare(key)==0)
Packit 8f70b4
	 return const_cast<KeyValueDB::Pair **>(p);
Packit 8f70b4
   }
Packit 8f70b4
   return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
const char *KeyValueDB::Lookup(const char *key) const
Packit 8f70b4
{
Packit 8f70b4
   const Pair * const*p=LookupPair(key);
Packit 8f70b4
   return p ? (*p)->value.get() : 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int KeyValueDB::Lock(int fd,int type)
Packit 8f70b4
{
Packit 8f70b4
   struct flock	lk;
Packit 8f70b4
   lk.l_type=type;
Packit 8f70b4
   lk.l_whence=0;
Packit 8f70b4
   lk.l_start=0;
Packit 8f70b4
   lk.l_len=0;
Packit 8f70b4
   int res=fcntl(fd,F_SETLK,&lk;;
Packit 8f70b4
   if(res==-1 && E_RETRY(errno))
Packit 8f70b4
   {
Packit 8f70b4
      int retries=5;
Packit 8f70b4
      bool echo=true;
Packit 8f70b4
      for(int i=0; i
Packit 8f70b4
      {
Packit 8f70b4
	 sleep(1);
Packit 8f70b4
	 if(echo && write(2,".",1)==-1)
Packit 8f70b4
	    echo=false;
Packit 8f70b4
	 res=fcntl(fd,F_SETLK,&lk;;
Packit 8f70b4
	 if(res==0)
Packit 8f70b4
	    break;
Packit 8f70b4
      }
Packit 8f70b4
      if(echo && write(2,"\r",1)==-1)
Packit 8f70b4
	 echo=false;
Packit 8f70b4
   }
Packit 8f70b4
   if(res==-1 && E_LOCK_IGNORE(errno))
Packit 8f70b4
      return 0;
Packit 8f70b4
   return res;
Packit 8f70b4
}