Blame src/ResMgr.cc

Packit 8f70b4
/*
Packit 8f70b4
 * lftp - file transfer program
Packit 8f70b4
 *
Packit 8f70b4
 * Copyright (c) 1996-2017 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
Packit 8f70b4
#include <fnmatch.h>
Packit 8f70b4
#include <ctype.h>
Packit 8f70b4
#include <unistd.h>
Packit 8f70b4
#include <stdlib.h>
Packit 8f70b4
#include <math.h>
Packit 8f70b4
#include <sys/types.h>
Packit 8f70b4
#include <sys/stat.h>
Packit 8f70b4
#include <errno.h>
Packit 8f70b4
#include <limits.h>
Packit 8f70b4
CDECL_BEGIN
Packit 8f70b4
#include "regex.h"
Packit 8f70b4
CDECL_END
Packit 8f70b4
#include "ResMgr.h"
Packit 8f70b4
#include "SMTask.h"
Packit 8f70b4
#include "misc.h"
Packit 8f70b4
#include "StringSet.h"
Packit 8f70b4
#include "log.h"
Packit 8f70b4
Packit 8f70b4
xlist_head<Resource> Resource::all_list;
Packit 8f70b4
xmap<ResType*> *ResType::types_by_name;
Packit 8f70b4
Packit 8f70b4
int ResType::VarNameCmp(const char *good_name,const char *name)
Packit 8f70b4
{
Packit 8f70b4
   int res=EXACT_PREFIX+EXACT_NAME;
Packit 8f70b4
   const char *colon=strchr(good_name,':');
Packit 8f70b4
   if(colon && !strchr(name,':'))
Packit 8f70b4
   {
Packit 8f70b4
      good_name=colon+1;
Packit 8f70b4
      res|=SUBSTR_PREFIX;
Packit 8f70b4
   }
Packit 8f70b4
   while(*good_name || *name)
Packit 8f70b4
   {
Packit 8f70b4
      if(*good_name==*name
Packit 8f70b4
      || (*good_name && *name && strchr("-_",*good_name) && strchr("-_",*name)))
Packit 8f70b4
      {
Packit 8f70b4
	 good_name++;
Packit 8f70b4
	 name++;
Packit 8f70b4
	 continue;
Packit 8f70b4
      }
Packit 8f70b4
      if(*name && !*good_name)
Packit 8f70b4
	 return DIFFERENT;
Packit 8f70b4
      if((!*name && *good_name)
Packit 8f70b4
      || (strchr("-_:",*name) && !strchr("-_:",*good_name)))
Packit 8f70b4
      {
Packit 8f70b4
	 good_name++;
Packit 8f70b4
	 if(strchr(name,':'))
Packit 8f70b4
	    res|=SUBSTR_PREFIX;
Packit 8f70b4
	 else
Packit 8f70b4
	    res|=SUBSTR_NAME;
Packit 8f70b4
	 continue;
Packit 8f70b4
      }
Packit 8f70b4
      return DIFFERENT;
Packit 8f70b4
   }
Packit 8f70b4
   return res;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
bool ResType::IsAlias() const
Packit 8f70b4
{
Packit 8f70b4
   return closure_valid==ResMgr::AliasValidate;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
const char *ResType::FindVar(const char *name,const ResType **type,const char **re_closure)
Packit 8f70b4
{
Packit 8f70b4
   const ResType *exact_proto=0;
Packit 8f70b4
   const ResType *exact_name=0;
Packit 8f70b4
   int sub=0;
Packit 8f70b4
Packit 8f70b4
   *type=types_by_name->lookup(name);
Packit 8f70b4
   if(*type)
Packit 8f70b4
      goto found; // exact match
Packit 8f70b4
Packit 8f70b4
   for(const ResType *type_scan=types_by_name->each_begin(); type_scan; type_scan=types_by_name->each_next())
Packit 8f70b4
   {
Packit 8f70b4
      switch(VarNameCmp(type_scan->name,name))
Packit 8f70b4
      {
Packit 8f70b4
      case EXACT_PREFIX+EXACT_NAME:
Packit 8f70b4
	 *type=type_scan;
Packit 8f70b4
	 return 0;
Packit 8f70b4
      case EXACT_PREFIX+SUBSTR_NAME:
Packit 8f70b4
	 if(!exact_proto && !exact_name)
Packit 8f70b4
	    sub=0;
Packit 8f70b4
	 exact_proto=*type=type_scan;
Packit 8f70b4
	 sub++;
Packit 8f70b4
	 break;
Packit 8f70b4
      case SUBSTR_PREFIX+EXACT_NAME:
Packit 8f70b4
	 if(!exact_proto && !exact_name)
Packit 8f70b4
	    sub=0;
Packit 8f70b4
	 exact_name=*type=type_scan;
Packit 8f70b4
	 sub++;
Packit 8f70b4
	 break;
Packit 8f70b4
      case SUBSTR_PREFIX+SUBSTR_NAME:
Packit 8f70b4
	 if(exact_proto || exact_name)
Packit 8f70b4
	    break;
Packit 8f70b4
	 sub++;
Packit 8f70b4
	 *type=type_scan;
Packit 8f70b4
	 break;
Packit 8f70b4
      default:
Packit 8f70b4
	 break;
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
   if(!*type && sub==0)
Packit 8f70b4
      return _("no such variable");
Packit 8f70b4
   if(sub==1)
Packit 8f70b4
      goto found;
Packit 8f70b4
   *type=0;
Packit 8f70b4
   return _("ambiguous variable name");
Packit 8f70b4
Packit 8f70b4
found:
Packit 8f70b4
   if((*type)->IsAlias()) {
Packit 8f70b4
      const char *alias_c=(*type)->GetAliasTarget();
Packit 8f70b4
      char *alias=alloca_strdup(alias_c);
Packit 8f70b4
      char *slash=strchr(alias,'/');
Packit 8f70b4
      if(slash) {
Packit 8f70b4
	 *slash=0;
Packit 8f70b4
	 if(re_closure)
Packit 8f70b4
	    *re_closure=alias_c+(slash+1-alias);
Packit 8f70b4
      }
Packit 8f70b4
      *type=types_by_name->lookup(alias);
Packit 8f70b4
      if(!*type)
Packit 8f70b4
	 return "invalid compatibility alias";
Packit 8f70b4
   }
Packit 8f70b4
   return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
const ResType *ResType::FindRes(const char *name)
Packit 8f70b4
{
Packit 8f70b4
   const ResType *type;
Packit 8f70b4
   const char *msg=FindVar(name,&type);
Packit 8f70b4
   if(msg)
Packit 8f70b4
      return 0;
Packit 8f70b4
   return type;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
const char *ResType::Set(const char *name,const char *cclosure,const char *cvalue,bool def)
Packit 8f70b4
{
Packit 8f70b4
   ResType *type;
Packit 8f70b4
   // find type of given variable
Packit 8f70b4
   const char *msg=FindVar(name,&type,&cclosure);
Packit 8f70b4
   if(msg)
Packit 8f70b4
      return msg;
Packit 8f70b4
Packit 8f70b4
   return type->Set(cclosure,cvalue,def);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
const char *ResType::Set(const char *cclosure,const char *cvalue,bool def)
Packit 8f70b4
{
Packit 8f70b4
   const char *msg=0;
Packit 8f70b4
Packit 8f70b4
   xstring_c value(cvalue);
Packit 8f70b4
   if(value && val_valid && (msg=val_valid(&value))!=0)
Packit 8f70b4
      return msg;
Packit 8f70b4
Packit 8f70b4
   xstring_c closure(cclosure);
Packit 8f70b4
   if((closure || closure_valid==ResMgr::HasClosure)
Packit 8f70b4
   && closure_valid && (msg=closure_valid(&closure))!=0)
Packit 8f70b4
      return msg;
Packit 8f70b4
Packit 8f70b4
   bool need_reconfig=false;
Packit 8f70b4
Packit 8f70b4
   xlist_for_each(Resource,*(type_value_list),node,scan)
Packit 8f70b4
   {
Packit 8f70b4
      // find the old value
Packit 8f70b4
      if(closure==scan->closure || !xstrcmp(scan->closure,closure))
Packit 8f70b4
      {
Packit 8f70b4
	 if(def)
Packit 8f70b4
	    return 0;
Packit 8f70b4
	 need_reconfig=true;
Packit 8f70b4
	 delete scan;
Packit 8f70b4
	 break;
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
   if(value)
Packit 8f70b4
   {
Packit 8f70b4
      (void)new Resource(this,closure,value,def);
Packit 8f70b4
      need_reconfig=true;
Packit 8f70b4
   }
Packit 8f70b4
   if(need_reconfig)
Packit 8f70b4
      ResClient::ReconfigAll(name);
Packit 8f70b4
   return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int ResMgr::ResourceCompare(const Resource *ar,const Resource *br)
Packit 8f70b4
{
Packit 8f70b4
   int diff=strcmp(ar->type->name,br->type->name);
Packit 8f70b4
   if(diff)
Packit 8f70b4
      return diff;
Packit 8f70b4
   if(ar->closure==br->closure)
Packit 8f70b4
      return 0;
Packit 8f70b4
   if(ar->closure==0)
Packit 8f70b4
      return -1;
Packit 8f70b4
   if(br->closure==0)
Packit 8f70b4
      return 1;
Packit 8f70b4
   return strcmp(ar->closure,br->closure);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Resource::Format(xstring& buf) const
Packit 8f70b4
{
Packit 8f70b4
   buf.appendf("set %s",type->name);
Packit 8f70b4
   const char *s=closure;
Packit 8f70b4
   if(s)
Packit 8f70b4
   {
Packit 8f70b4
      buf.append('/');
Packit 8f70b4
      bool par=false;
Packit 8f70b4
      if(strcspn(s," \t>|;&")!=strlen(s))
Packit 8f70b4
	 par=true;
Packit 8f70b4
      if(par)
Packit 8f70b4
	 buf.append('"');
Packit 8f70b4
      while(*s)
Packit 8f70b4
      {
Packit 8f70b4
	 if(strchr("\"\\",*s))
Packit 8f70b4
	    buf.append('\\');
Packit 8f70b4
	 buf.append(*s++);
Packit 8f70b4
      }
Packit 8f70b4
      if(par)
Packit 8f70b4
	 buf.append('"');
Packit 8f70b4
   }
Packit 8f70b4
   buf.append(' ');
Packit 8f70b4
   s=value;
Packit 8f70b4
Packit 8f70b4
   bool par=false;
Packit 8f70b4
   if(*s==0 || strcspn(s," \t>|;&")!=strlen(s))
Packit 8f70b4
      par=true;
Packit 8f70b4
   if(par)
Packit 8f70b4
      buf.append('"');
Packit 8f70b4
   while(*s)
Packit 8f70b4
   {
Packit 8f70b4
      if(strchr("\"\\",*s))
Packit 8f70b4
	 buf.append('\\');
Packit 8f70b4
      buf.append(*s++);
Packit 8f70b4
   }
Packit 8f70b4
   if(par)
Packit 8f70b4
      buf.append('"');
Packit 8f70b4
   buf.append('\n');
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
static int PResourceCompare(const Resource *const*a,const Resource *const*b)
Packit 8f70b4
{
Packit 8f70b4
   return ResMgr::ResourceCompare(*a,*b);
Packit 8f70b4
}
Packit 8f70b4
static int RefResourceCompare(const Ref<Resource> *a,const Ref<Resource> *b)
Packit 8f70b4
{
Packit 8f70b4
   return ResMgr::ResourceCompare(*a,*b);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
char *ResType::Format(bool with_defaults,bool only_defaults)
Packit 8f70b4
{
Packit 8f70b4
   RefArray<Resource> created;
Packit 8f70b4
   if(with_defaults || only_defaults)
Packit 8f70b4
   {
Packit 8f70b4
      for(ResType *dscan=types_by_name->each_begin(); dscan; dscan=types_by_name->each_next())
Packit 8f70b4
	 if((only_defaults || dscan->SimpleQuery(0)==0) && !dscan->IsAlias())
Packit 8f70b4
	    created.append(new Resource(dscan,
Packit 8f70b4
	       0,xstrdup(dscan->defvalue?dscan->defvalue:"(nil)")));
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   xstring buf("");
Packit 8f70b4
Packit 8f70b4
   if(!only_defaults)
Packit 8f70b4
   {
Packit 8f70b4
      // just created Resources are also in all_list.
Packit 8f70b4
      xarray<const Resource*> arr;
Packit 8f70b4
      xlist_for_each(Resource,Resource::all_list,node,scan) {
Packit 8f70b4
	 if(!scan->def || with_defaults)
Packit 8f70b4
	    arr.append(scan);
Packit 8f70b4
      }
Packit 8f70b4
      arr.qsort(PResourceCompare);
Packit 8f70b4
      for(int i=0; i
Packit 8f70b4
	 arr[i]->Format(buf);
Packit 8f70b4
   }
Packit 8f70b4
   else
Packit 8f70b4
   {
Packit 8f70b4
      created.qsort(RefResourceCompare);
Packit 8f70b4
      for(int i=0; i
Packit 8f70b4
	 created[i]->Format(buf);
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   return buf.borrow();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
char **ResType::Generator(void)
Packit 8f70b4
{
Packit 8f70b4
   StringSet res;
Packit 8f70b4
Packit 8f70b4
   for(ResType *dscan=types_by_name->each_begin(); dscan; dscan=types_by_name->each_next())
Packit 8f70b4
      if(!dscan->IsAlias())
Packit 8f70b4
	 res.Append(dscan->name);
Packit 8f70b4
Packit 8f70b4
   res.qsort();
Packit 8f70b4
   return res.borrow();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
const char *ResMgr::BoolValidate(xstring_c *value)
Packit 8f70b4
{
Packit 8f70b4
   const char *v=*value;
Packit 8f70b4
   const char *newval=0;
Packit 8f70b4
Packit 8f70b4
   switch(v[0])
Packit 8f70b4
   {
Packit 8f70b4
   case 't':   newval="true";	 break;
Packit 8f70b4
   case 'T':   newval="True";	 break;
Packit 8f70b4
   case 'f':   newval="false";	 break;
Packit 8f70b4
   case 'F':   newval="False";	 break;
Packit 8f70b4
   case 'y':   newval="yes";	 break;
Packit 8f70b4
   case 'Y':   newval="Yes";	 break;
Packit 8f70b4
   case 'n':   newval="no";	 break;
Packit 8f70b4
   case 'N':   newval="No";	 break;
Packit 8f70b4
   case '1':   newval="1";	 break;
Packit 8f70b4
   case '0':   newval="0";	 break;
Packit 8f70b4
   case '+':   newval="+";	 break;
Packit 8f70b4
   case '-':   newval="-";	 break;
Packit 8f70b4
   case 'o':   newval=(v[1]=='f' || v[1]=='F')?"off":"on";  break;
Packit 8f70b4
   case 'O':   newval=(v[1]=='f' || v[1]=='F')?"Off":"On";  break;
Packit 8f70b4
   default:
Packit 8f70b4
      return _("invalid boolean value");
Packit 8f70b4
   }
Packit 8f70b4
   if(strcmp(v,newval))
Packit 8f70b4
      value->set(newval);
Packit 8f70b4
Packit 8f70b4
   return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
const char *ResMgr::TriBoolValidate(xstring_c *value)
Packit 8f70b4
{
Packit 8f70b4
   if(!BoolValidate(value))
Packit 8f70b4
      return 0;
Packit 8f70b4
Packit 8f70b4
   /* not bool */
Packit 8f70b4
   const char *v=*value;
Packit 8f70b4
   const char *newval=0;
Packit 8f70b4
Packit 8f70b4
   switch(v[0])
Packit 8f70b4
   {
Packit 8f70b4
   case 'a':   newval="auto";	 break;
Packit 8f70b4
   case 'A':   newval="Auto";	 break;
Packit 8f70b4
   default:
Packit 8f70b4
      return _("invalid boolean/auto value");
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   if(strcmp(v,newval))
Packit 8f70b4
      value->set(newval);
Packit 8f70b4
Packit 8f70b4
   return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
static const char power_letter[] =
Packit 8f70b4
{
Packit 8f70b4
  0,	/* not used */
Packit 8f70b4
  'K',	/* kibi ('k' for kilo is a special case) */
Packit 8f70b4
  'M',	/* mega or mebi */
Packit 8f70b4
  'G',	/* giga or gibi */
Packit 8f70b4
  'T',	/* tera or tebi */
Packit 8f70b4
  'P',	/* peta or pebi */
Packit 8f70b4
  'E',	/* exa or exbi */
Packit 8f70b4
  'Z',	/* zetta or 2**70 */
Packit 8f70b4
  'Y'	/* yotta or 2**80 */
Packit 8f70b4
};
Packit 8f70b4
static unsigned long long get_power_multiplier(char p)
Packit 8f70b4
{
Packit 8f70b4
   const char *scan=power_letter;
Packit 8f70b4
   const int scale=1024;
Packit 8f70b4
   unsigned long long mul=1;
Packit 8f70b4
   p=toupper(p);
Packit 8f70b4
   while(scan
Packit 8f70b4
      if(p==*scan)
Packit 8f70b4
	 return mul;
Packit 8f70b4
      mul*=scale;
Packit 8f70b4
      scan++;
Packit 8f70b4
   }
Packit 8f70b4
   return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
const char *ResMgr::NumberValidate(xstring_c *value)
Packit 8f70b4
{
Packit 8f70b4
   const char *v=*value;
Packit 8f70b4
   const char *end=v;
Packit 8f70b4
Packit 8f70b4
   (void)strtoll(v,const_cast<char**>(&end),0);
Packit 8f70b4
   unsigned long long m=get_power_multiplier(*end);
Packit 8f70b4
Packit 8f70b4
   if(v==end || m==0 || end[m>1])
Packit 8f70b4
      return _("invalid number");
Packit 8f70b4
Packit 8f70b4
   return 0;
Packit 8f70b4
}
Packit 8f70b4
const char *ResMgr::FloatValidate(xstring_c *value)
Packit 8f70b4
{
Packit 8f70b4
   const char *v=*value;
Packit 8f70b4
   const char *end=v;
Packit 8f70b4
Packit 8f70b4
   (void)strtod(v,const_cast<char**>(&end));
Packit 8f70b4
   unsigned long long m=get_power_multiplier(*end);
Packit 8f70b4
Packit 8f70b4
   if(v==end || m==0 || end[m>1])
Packit 8f70b4
      return _("invalid floating point number");
Packit 8f70b4
Packit 8f70b4
   return 0;
Packit 8f70b4
}
Packit 8f70b4
const char *ResMgr::UNumberValidate(xstring_c *value)
Packit 8f70b4
{
Packit 8f70b4
   const char *v=*value;
Packit 8f70b4
   const char *end=v;
Packit 8f70b4
Packit 8f70b4
   (void)strtoull(v,const_cast<char**>(&end),0);
Packit 8f70b4
   unsigned long long m=get_power_multiplier(*end);
Packit 8f70b4
Packit 8f70b4
   if(!isdigit((unsigned char)v[0])
Packit 8f70b4
   || v==end || m==0 || end[m>1])
Packit 8f70b4
      return _("invalid unsigned number");
Packit 8f70b4
Packit 8f70b4
   return 0;
Packit 8f70b4
}
Packit 8f70b4
const char *ResMgr::AliasValidate(xstring_c *)
Packit 8f70b4
{
Packit 8f70b4
   return "";
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
unsigned long long ResValue::to_unumber(unsigned long long max) const
Packit 8f70b4
{
Packit 8f70b4
   if (is_nil())
Packit 8f70b4
      return 0;
Packit 8f70b4
   const char *end=s;
Packit 8f70b4
   unsigned long long v=strtoull(s,const_cast<char**>(&end),0);
Packit 8f70b4
   unsigned long long m=get_power_multiplier(*end);
Packit 8f70b4
   unsigned long long vm=v*m;
Packit 8f70b4
   if(vm/m!=v || vm>max)
Packit 8f70b4
      return max;
Packit 8f70b4
   return vm;
Packit 8f70b4
}
Packit 8f70b4
long long ResValue::to_number(long long min,long long max) const
Packit 8f70b4
{
Packit 8f70b4
   if (is_nil())
Packit 8f70b4
      return 0;
Packit 8f70b4
   const char *end=s;
Packit 8f70b4
   long long v=strtoll(s,const_cast<char**>(&end),0);
Packit 8f70b4
   long long m=get_power_multiplier(*end);
Packit 8f70b4
   long long vm=v*m;
Packit 8f70b4
   if(vm/m!=v)
Packit 8f70b4
      return v>0?max:min;
Packit 8f70b4
   if(vm>max)
Packit 8f70b4
      return max;
Packit 8f70b4
   if(vm
Packit 8f70b4
      return min;
Packit 8f70b4
   return vm;
Packit 8f70b4
}
Packit 8f70b4
ResValue::operator int() const
Packit 8f70b4
{
Packit 8f70b4
   return to_number(INT_MIN,INT_MAX);
Packit 8f70b4
}
Packit 8f70b4
ResValue::operator long() const
Packit 8f70b4
{
Packit 8f70b4
   return to_number(LONG_MIN,LONG_MAX);
Packit 8f70b4
}
Packit 8f70b4
ResValue::operator unsigned() const
Packit 8f70b4
{
Packit 8f70b4
   return to_unumber(UINT_MAX);
Packit 8f70b4
}
Packit 8f70b4
ResValue::operator unsigned long() const
Packit 8f70b4
{
Packit 8f70b4
   return to_unumber(ULONG_MAX);
Packit 8f70b4
}
Packit 8f70b4
bool ResValue::to_tri_bool(bool a) const
Packit 8f70b4
{
Packit 8f70b4
   if(*s=='a' || *s=='A')
Packit 8f70b4
      return a;
Packit 8f70b4
   return to_bool();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
Resource::Resource(ResType *type,const char *closure,const char *value,bool def)
Packit 8f70b4
   : type(type), value(value), closure(closure), def(def), all_node(this), type_value_node(this)
Packit 8f70b4
{
Packit 8f70b4
   all_list.add_tail(all_node);
Packit 8f70b4
   type->type_value_list->add_tail(type_value_node);
Packit 8f70b4
}
Packit 8f70b4
Resource::~Resource()
Packit 8f70b4
{
Packit 8f70b4
   all_node.remove();
Packit 8f70b4
   type_value_node.remove();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
bool Resource::ClosureMatch(const char *cl_data)
Packit 8f70b4
{
Packit 8f70b4
   if(!closure && !cl_data)
Packit 8f70b4
      return true;
Packit 8f70b4
   if(!(closure && cl_data))
Packit 8f70b4
      return false;
Packit 8f70b4
   // a special case for domain name match (i.e. example.org matches *.example.org)
Packit 8f70b4
   if(closure[0]=='*' && closure[1]=='.' && !strcmp(closure+2,cl_data))
Packit 8f70b4
      return true;
Packit 8f70b4
   if(0==fnmatch(closure,cl_data,FNM_PATHNAME))
Packit 8f70b4
      return true;
Packit 8f70b4
   // try to match basename; helps matching torrent metadata url to *.torrent
Packit 8f70b4
   const char *bn=basename_ptr(cl_data);
Packit 8f70b4
   if(bn!=cl_data && 0==fnmatch(closure,bn,FNM_PATHNAME))
Packit 8f70b4
      return true;
Packit 8f70b4
   return false;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
const char *ResMgr::QueryNext(const char *name,const char **closure,Resource **ptr)
Packit 8f70b4
{
Packit 8f70b4
   xlist<Resource> *node=0;
Packit 8f70b4
   if(*ptr==0)
Packit 8f70b4
   {
Packit 8f70b4
      const ResType *type=FindRes(name);
Packit 8f70b4
      if(!type) {
Packit 8f70b4
	 *ptr=0;
Packit 8f70b4
	 *closure=0;
Packit 8f70b4
	 return 0;
Packit 8f70b4
      }
Packit 8f70b4
      node=type->type_value_list->get_next();
Packit 8f70b4
   }
Packit 8f70b4
   else
Packit 8f70b4
   {
Packit 8f70b4
      node=(*ptr)->type_value_node.get_next();
Packit 8f70b4
   }
Packit 8f70b4
   *ptr=node->get_obj();
Packit 8f70b4
   if(*ptr) {
Packit 8f70b4
      *closure=(*ptr)->closure;
Packit 8f70b4
      return (*ptr)->value;
Packit 8f70b4
   }
Packit 8f70b4
   *closure=0;
Packit 8f70b4
   return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
const char *ResType::SimpleQuery(const char *closure) const
Packit 8f70b4
{
Packit 8f70b4
   // find the value
Packit 8f70b4
   xlist_for_each(Resource,*(type_value_list),node,scan)
Packit 8f70b4
      if(scan->ClosureMatch(closure))
Packit 8f70b4
	 return scan->value;
Packit 8f70b4
   return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
ResValue ResMgr::Query(const char *name,const char *closure)
Packit 8f70b4
{
Packit 8f70b4
   const char *msg;
Packit 8f70b4
Packit 8f70b4
   const ResType *type;
Packit 8f70b4
   // find type of given variable
Packit 8f70b4
   msg=FindVar(name,&type);
Packit 8f70b4
   if(msg)
Packit 8f70b4
   {
Packit 8f70b4
      // debug only
Packit 8f70b4
      // fprintf(stderr,_("Query of variable `%s' failed: %s\n"),name,msg);
Packit 8f70b4
      return 0;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   return type->Query(closure);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
ResValue ResType::Query(const char *closure) const
Packit 8f70b4
{
Packit 8f70b4
   const char *v=0;
Packit 8f70b4
Packit 8f70b4
   if(closure)
Packit 8f70b4
      v=SimpleQuery(closure);
Packit 8f70b4
   if(!v)
Packit 8f70b4
      v=SimpleQuery(0);
Packit 8f70b4
   if(!v)
Packit 8f70b4
      v=defvalue;
Packit 8f70b4
Packit 8f70b4
   return v;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
bool ResMgr::str2bool(const char *s)
Packit 8f70b4
{
Packit 8f70b4
   return(strchr("TtYy1+",s[0])!=0 || !strcasecmp(s,"on"));
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
ResDecl::ResDecl(const char *a_name,const char *a_defvalue,ResValValid *a_val_valid,ResClValid *a_closure_valid)
Packit 8f70b4
{
Packit 8f70b4
   name=a_name;
Packit 8f70b4
   defvalue=a_defvalue;
Packit 8f70b4
   val_valid=a_val_valid;
Packit 8f70b4
   closure_valid=a_closure_valid;
Packit 8f70b4
   Register();
Packit 8f70b4
}
Packit 8f70b4
ResDecls::ResDecls(ResType *array)
Packit 8f70b4
{
Packit 8f70b4
   while(array->name)
Packit 8f70b4
      array++->Register();
Packit 8f70b4
}
Packit 8f70b4
ResDecls::ResDecls(ResType *r1,ResType *r2,...)
Packit 8f70b4
{
Packit 8f70b4
   r.append(r1);
Packit 8f70b4
   r1->Register();
Packit 8f70b4
   if(!r2)
Packit 8f70b4
      return;
Packit 8f70b4
   r.append(r2);
Packit 8f70b4
   r2->Register();
Packit 8f70b4
   va_list v;
Packit 8f70b4
   va_start(v,r2);
Packit 8f70b4
   while((r1=va_arg(v,ResType *))!=0)
Packit 8f70b4
   {
Packit 8f70b4
      r1->Register();
Packit 8f70b4
      r.append(r1);
Packit 8f70b4
   }
Packit 8f70b4
   va_end(v);
Packit 8f70b4
}
Packit 8f70b4
ResDecls::~ResDecls()
Packit 8f70b4
{
Packit 8f70b4
   for(int i=0; i
Packit 8f70b4
      r[i]->Unregister();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void ResType::Register()
Packit 8f70b4
{
Packit 8f70b4
   if(!types_by_name)
Packit 8f70b4
      types_by_name=new xmap<ResType*>;
Packit 8f70b4
   types_by_name->add(name,this);
Packit 8f70b4
   if(!type_value_list)
Packit 8f70b4
      type_value_list=new xlist_head<Resource>();
Packit 8f70b4
}
Packit 8f70b4
void ResType::Unregister()
Packit 8f70b4
{
Packit 8f70b4
   if(types_by_name)
Packit 8f70b4
      types_by_name->remove(name);
Packit 8f70b4
   if(type_value_list) {
Packit 8f70b4
      // remove all resources of this type
Packit 8f70b4
      xlist_for_each_safe(Resource,*type_value_list,node,scan,next)
Packit 8f70b4
	 delete scan;
Packit 8f70b4
      delete type_value_list;
Packit 8f70b4
      type_value_list=0;
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void TimeIntervalR::init(const char *s)
Packit 8f70b4
{
Packit 8f70b4
   double interval=0;
Packit 8f70b4
   infty=false;
Packit 8f70b4
   error_text=0;
Packit 8f70b4
Packit 8f70b4
   if(!strncasecmp(s,"inf",3)
Packit 8f70b4
   || !strcasecmp(s,"forever")
Packit 8f70b4
   || !strcasecmp(s,"never"))
Packit 8f70b4
   {
Packit 8f70b4
      infty=true;
Packit 8f70b4
      return;
Packit 8f70b4
   }
Packit 8f70b4
   int pos=0;
Packit 8f70b4
   for(;;)
Packit 8f70b4
   {
Packit 8f70b4
      double prec;
Packit 8f70b4
      char ch='s';
Packit 8f70b4
      int pos1=strlen(s+pos);
Packit 8f70b4
      int n=sscanf(s+pos,"%lf%c%n",&prec,&ch,&pos1);
Packit 8f70b4
      if(n<1)
Packit 8f70b4
	 break;
Packit 8f70b4
      ch=tolower((unsigned char)ch);
Packit 8f70b4
      if(ch=='m')
Packit 8f70b4
	 prec*=MINUTE;
Packit 8f70b4
      else if(ch=='h')
Packit 8f70b4
	 prec*=HOUR;
Packit 8f70b4
      else if(ch=='d')
Packit 8f70b4
	 prec*=DAY;
Packit 8f70b4
      else if(ch!='s')
Packit 8f70b4
      {
Packit 8f70b4
	 error_text=_("Invalid time unit letter, only [smhd] are allowed.");
Packit 8f70b4
	 return;
Packit 8f70b4
      }
Packit 8f70b4
      interval+=prec;
Packit 8f70b4
      pos+=pos1;
Packit 8f70b4
   }
Packit 8f70b4
   if(pos==0)
Packit 8f70b4
   {
Packit 8f70b4
      error_text=_("Invalid time format. Format is <time><unit>, e.g. 2h30m.");
Packit 8f70b4
      return;
Packit 8f70b4
   }
Packit 8f70b4
   TimeDiff::Set(interval);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
const char *ResMgr::TimeIntervalValidate(xstring_c *s)
Packit 8f70b4
{
Packit 8f70b4
   TimeIntervalR t(*s);
Packit 8f70b4
   if(t.Error())
Packit 8f70b4
      return t.ErrorText();
Packit 8f70b4
   return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void NumberPair::init(char sep1,const char *s)
Packit 8f70b4
{
Packit 8f70b4
   sep=sep1;
Packit 8f70b4
   Set(s);
Packit 8f70b4
}
Packit 8f70b4
long long NumberPair::parse1(const char *s)
Packit 8f70b4
{
Packit 8f70b4
   if(!s || !*s)
Packit 8f70b4
      return 0;
Packit 8f70b4
   const char *end=s;
Packit 8f70b4
   long long v=strtoll(s,const_cast<char**>(&end),0);
Packit 8f70b4
   long long m=get_power_multiplier(*end);
Packit 8f70b4
   if(s==end || m==0 || end[m>1]) {
Packit 8f70b4
      error_text=_("invalid number");
Packit 8f70b4
      return 0;
Packit 8f70b4
   }
Packit 8f70b4
   long long vm=v*m;
Packit 8f70b4
   if(vm/m!=v) {
Packit 8f70b4
      error_text=_("integer overflow");
Packit 8f70b4
      return 0;
Packit 8f70b4
   }
Packit 8f70b4
   return vm;
Packit 8f70b4
}
Packit 8f70b4
void NumberPair::Set(const char *s0)
Packit 8f70b4
{
Packit 8f70b4
   n1=n2=0;
Packit 8f70b4
   no_n1=no_n2=true;
Packit 8f70b4
   error_text=0;
Packit 8f70b4
Packit 8f70b4
   if(!s0)
Packit 8f70b4
      return;
Packit 8f70b4
Packit 8f70b4
   char *s1=alloca_strdup(s0);
Packit 8f70b4
   char *s2=s1;
Packit 8f70b4
   while(*s2 && *s2!=sep && *s2!=':')
Packit 8f70b4
      s2++;
Packit 8f70b4
   if(*s2)
Packit 8f70b4
      *s2++=0;
Packit 8f70b4
   else
Packit 8f70b4
      s2=0;
Packit 8f70b4
Packit 8f70b4
   n1=parse1(s1);
Packit 8f70b4
   no_n1=!*s1;
Packit 8f70b4
   n2=(s2?parse1(s2):n1);
Packit 8f70b4
   no_n2=(s2 && !*s2);
Packit 8f70b4
Packit 8f70b4
   if(!error_text && Log::global) {
Packit 8f70b4
      Log::global->Format(10,"%s translated to pair %lld%c%lld (%d,%d)\n",
Packit 8f70b4
	 s0,n1,sep,n2,no_n1,no_n2);
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
Range::Range(const char *s) : NumberPair('-')
Packit 8f70b4
{
Packit 8f70b4
   if(!strcasecmp(s,"full") || !strcasecmp(s,"any"))
Packit 8f70b4
      return;
Packit 8f70b4
   Set(s);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
long long Range::Random()
Packit 8f70b4
{
Packit 8f70b4
   random_init();
Packit 8f70b4
Packit 8f70b4
   if(no_n1 && no_n2)
Packit 8f70b4
      return random();
Packit 8f70b4
   if(no_n2)
Packit 8f70b4
      return n1+random();
Packit 8f70b4
Packit 8f70b4
   return n1 + (long long)((n2-n1+1)*random01());
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
const char *ResMgr::RangeValidate(xstring_c *s)
Packit 8f70b4
{
Packit 8f70b4
   Range r(*s);
Packit 8f70b4
   if(r.Error())
Packit 8f70b4
      return r.ErrorText();
Packit 8f70b4
   char *colon=strchr(s->get_non_const(),':');
Packit 8f70b4
   if(colon)
Packit 8f70b4
      *colon='-';
Packit 8f70b4
   return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
const char *ResMgr::ERegExpValidate(xstring_c *s)
Packit 8f70b4
{
Packit 8f70b4
   if(**s==0)
Packit 8f70b4
      return 0;
Packit 8f70b4
   regex_t re;
Packit 8f70b4
   int err=regcomp(&re,*s,REG_EXTENDED|REG_NOSUB);
Packit 8f70b4
   if(err)
Packit 8f70b4
   {
Packit 8f70b4
      const int max_err_len=128;
Packit 8f70b4
      char *err_msg=xstring::tmp_buf(max_err_len);
Packit 8f70b4
      regerror(err,0,err_msg,max_err_len);
Packit 8f70b4
      return err_msg;
Packit 8f70b4
   }
Packit 8f70b4
   regfree(&re);
Packit 8f70b4
   return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
const char *ResMgr::IPv4AddrValidate(xstring_c *value)
Packit 8f70b4
{
Packit 8f70b4
   if(!**value)
Packit 8f70b4
      return 0;
Packit 8f70b4
   if(!is_ipv4_address(*value))
Packit 8f70b4
      return _("Invalid IPv4 numeric address");
Packit 8f70b4
   return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
#if INET6
Packit 8f70b4
const char *ResMgr::IPv6AddrValidate(xstring_c *value)
Packit 8f70b4
{
Packit 8f70b4
   if(!**value)
Packit 8f70b4
      return 0;
Packit 8f70b4
   if(!is_ipv6_address(*value))
Packit 8f70b4
      return _("Invalid IPv6 numeric address");
Packit 8f70b4
   return 0;
Packit 8f70b4
}
Packit 8f70b4
#endif
Packit 8f70b4
Packit 8f70b4
const char *ResMgr::FileAccessible(xstring_c *value,int mode,bool want_dir)
Packit 8f70b4
{
Packit 8f70b4
   if(!**value)
Packit 8f70b4
      return 0;
Packit 8f70b4
   const char *f=expand_home_relative(*value);
Packit 8f70b4
   xstring_c cwd;
Packit 8f70b4
   const char *error=0;
Packit 8f70b4
   if(f[0]!='/')
Packit 8f70b4
   {
Packit 8f70b4
      xgetcwd_to(cwd);
Packit 8f70b4
      if(cwd)
Packit 8f70b4
	 f=dir_file(cwd,f);
Packit 8f70b4
   }
Packit 8f70b4
   struct stat st;
Packit 8f70b4
   if(stat(f,&st)<0)
Packit 8f70b4
      error=strerror(errno);
Packit 8f70b4
   else if(want_dir ^ S_ISDIR(st.st_mode))
Packit 8f70b4
      error=strerror(errno=want_dir?ENOTDIR:EISDIR);
Packit 8f70b4
   else if(access(f,mode)<0)
Packit 8f70b4
      error=strerror(errno);
Packit 8f70b4
   else
Packit 8f70b4
      value->set(f);
Packit 8f70b4
   return error;
Packit 8f70b4
}
Packit 8f70b4
const char *ResMgr::FileReadable(xstring_c *value)
Packit 8f70b4
{
Packit 8f70b4
   return FileAccessible(value,R_OK);
Packit 8f70b4
}
Packit 8f70b4
const char *ResMgr::FileExecutable(xstring_c *value)
Packit 8f70b4
{
Packit 8f70b4
   return FileAccessible(value,X_OK);
Packit 8f70b4
}
Packit 8f70b4
const char *ResMgr::DirReadable(xstring_c *value)
Packit 8f70b4
{
Packit 8f70b4
   return FileAccessible(value,R_OK|X_OK,true);
Packit 8f70b4
}
Packit 8f70b4
const char *ResMgr::FileCreatable(xstring_c *value)
Packit 8f70b4
{
Packit 8f70b4
   if(!**value)
Packit 8f70b4
      return 0;
Packit 8f70b4
   const char *error=FileAccessible(value,W_OK,false);
Packit 8f70b4
   if(!error || (error && errno!=ENOENT))
Packit 8f70b4
      return error;
Packit 8f70b4
   const char *bn=basename_ptr(*value);
Packit 8f70b4
   xstring_c dir(dirname(*value));
Packit 8f70b4
   if(!*dir)
Packit 8f70b4
      xgetcwd_to(dir);
Packit 8f70b4
   error=FileAccessible(&dir,X_OK|W_OK,true);
Packit 8f70b4
   if(!error)  // dir may be expanded, combine it with base file name.
Packit 8f70b4
      value->set(dir_file(dir,bn));
Packit 8f70b4
   return error;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
#ifdef HAVE_ICONV
Packit 8f70b4
CDECL_BEGIN
Packit 8f70b4
# include <iconv.h>
Packit 8f70b4
CDECL_END
Packit 8f70b4
#endif
Packit 8f70b4
const char *ResMgr::CharsetValidate(xstring_c *value)
Packit 8f70b4
{
Packit 8f70b4
   if(!**value)
Packit 8f70b4
      return 0;
Packit 8f70b4
#ifdef HAVE_ICONV
Packit 8f70b4
   iconv_t ic=iconv_open(*value,*value);
Packit 8f70b4
   if(ic==(iconv_t)-1)
Packit 8f70b4
      return _("this encoding is not supported");
Packit 8f70b4
   iconv_close(ic);
Packit 8f70b4
   return 0;
Packit 8f70b4
#else
Packit 8f70b4
   return _("this encoding is not supported");
Packit 8f70b4
#endif
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
const char *ResMgr::NoClosure(xstring_c *)
Packit 8f70b4
{
Packit 8f70b4
   return _("no closure defined for this setting");
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
const char *ResMgr::HasClosure(xstring_c *c)
Packit 8f70b4
{
Packit 8f70b4
   if(!*c || !**c)
Packit 8f70b4
      return _("a closure is required for this setting");
Packit 8f70b4
   return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
const char *ResMgr::UNumberPairValidate(xstring_c *value)
Packit 8f70b4
{
Packit 8f70b4
   NumberPair pair(':',*value);
Packit 8f70b4
   if(pair.Error())
Packit 8f70b4
      return pair.ErrorText();
Packit 8f70b4
   return 0;
Packit 8f70b4
}
Packit 8f70b4
void ResValue::ToNumberPair(int &a,int &b) const
Packit 8f70b4
{
Packit 8f70b4
   NumberPair pair(':',s);
Packit 8f70b4
   if(pair.Error()) {
Packit 8f70b4
      a=b=0;
Packit 8f70b4
   } else {
Packit 8f70b4
      a=pair.N1();
Packit 8f70b4
      b=pair.HasN2()?pair.N2():a;
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
xlist_head<ResClient> ResClient::list;
Packit 8f70b4
ResValue ResClient::Query(const char *name,const char *closure) const
Packit 8f70b4
{
Packit 8f70b4
   if(!strchr(name,':'))
Packit 8f70b4
   {
Packit 8f70b4
      const char *prefix=ResPrefix();
Packit 8f70b4
      name=xstring::cat(prefix,":",name,NULL);
Packit 8f70b4
      name=alloca_strdup(name);
Packit 8f70b4
   }
Packit 8f70b4
   if(!closure)
Packit 8f70b4
      closure=ResClosure();
Packit 8f70b4
   return ResMgr::Query(name,closure);
Packit 8f70b4
}
Packit 8f70b4
ResClient::ResClient()
Packit 8f70b4
   : node(this)
Packit 8f70b4
{
Packit 8f70b4
   list.add(node);
Packit 8f70b4
}
Packit 8f70b4
ResClient::~ResClient()
Packit 8f70b4
{
Packit 8f70b4
   node.remove();
Packit 8f70b4
}
Packit 8f70b4
void ResClient::ReconfigAll(const char *r)
Packit 8f70b4
{
Packit 8f70b4
   xlist_for_each(ResClient,list,node,scan)
Packit 8f70b4
      scan->Reconfig(r);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
bool ResType::QueryBool(const char *closure) const
Packit 8f70b4
{
Packit 8f70b4
   return Query(closure).to_bool();
Packit 8f70b4
}
Packit 8f70b4
bool ResMgr::QueryBool(const char *name,const char *closure)
Packit 8f70b4
{
Packit 8f70b4
   return Query(name,closure).to_bool();
Packit 8f70b4
}
Packit 8f70b4
bool ResClient::QueryBool(const char *name,const char *closure) const
Packit 8f70b4
{
Packit 8f70b4
   return Query(name,closure).to_bool();
Packit 8f70b4
}
Packit 8f70b4
bool ResType::QueryTriBool(const char *closure,bool a) const
Packit 8f70b4
{
Packit 8f70b4
   return Query(closure).to_tri_bool(a);
Packit 8f70b4
}
Packit 8f70b4
bool ResMgr::QueryTriBool(const char *name,const char *closure,bool a)
Packit 8f70b4
{
Packit 8f70b4
   return Query(name,closure).to_tri_bool(a);
Packit 8f70b4
}
Packit 8f70b4
bool ResClient::QueryTriBool(const char *name,const char *closure,bool a) const
Packit 8f70b4
{
Packit 8f70b4
   return Query(name,closure).to_tri_bool(a);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void ResType::ClassCleanup()
Packit 8f70b4
{
Packit 8f70b4
   xlist_for_each_safe(Resource,Resource::all_list,node,scan,next)
Packit 8f70b4
      delete scan;
Packit 8f70b4
   if(types_by_name) {
Packit 8f70b4
      for(ResType *t=types_by_name->each_begin(); t; t=types_by_name->each_next())
Packit 8f70b4
	 t->Unregister();
Packit 8f70b4
      delete types_by_name; types_by_name=0;
Packit 8f70b4
   }
Packit 8f70b4
}