Current section
Files
Jump to
Current section
Files
c_src/dclass/openddr_client.c
/*
* Copyright 2012 The Weather Channel
* Copyright 2013 Reza Naghibi
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#include "dclass_client.h"
//openddr property keys
static const char *openddr_key_array[] = OPENDDR_KEYS;
static int openddr_read_device_raw(FILE*,dtree_dt_index*,dtree_dt_index*);
static int openddr_read_pattern(FILE*,dtree_dt_index*,dtree_dt_index*,dtree_flag_f*);
static int openddr_add_device_pattern(dtree_dt_index*,dtree_dt_index*,dclass_keyvalue*,dclass_keyvalue*,char*,dtree_flag_f);
static const char *openddr_copy_string(dtree_dt_index*,const char*,const char*);
static int openddr_alloc_kvd(dtree_dt_index*,dtree_dt_index*,char*);
static char *openddr_get_attr(const char*,const char*,dtree_dt_index*,char**,char*);
static char *openddr_get_value(const char*,const char*,dtree_dt_index*,char**,char*);
static char *openddr_get_str(const char*,const char*,dtree_dt_index*,char**,char*,char*,char);
static char *openddr_unregex(const char*,char*);
#if OPENDDR_BLKBRY_FIX
static void openddr_convert_bb(char*);
#endif
extern char **dclass_get_value_pos(dclass_keyvalue*,char*);
//loads openddr resources
int openddr_load_resources(dclass_index *di,const char *path)
{
int i;
int ret;
int dcount=0;
int pcount=0;
char fpath[1024];
FILE *f=NULL;
dtree_flag_f flags=0;
dtree_dt_index dev;
dtree_dt_index *h=&di->dti;
dclass_init_index(di);
dtree_init_index(&dev);
h->sflags=DTREE_S_FLAG_REGEX|DTREE_S_FLAG_DUPS;
//string and device cache
di->error.id=dtree_alloc_string(h,"unknown",7);
if(!openddr_alloc_kvd(h,&dev,"genericPhone") || !openddr_alloc_kvd(h,&dev,"genericTouchPhone") ||
!openddr_alloc_kvd(h,&dev,"desktopDevice"))
goto dexit;
dtree_alloc_string(h,"true",4);
dtree_alloc_string(h,"false",5);
//build a device tree
for(i=0;i<2;i++)
{
if(!i)
sprintf(fpath,"%s/%s",path,OPENDDR_RSRC_DD);
else
sprintf(fpath,"%s/%s",path,OPENDDR_RSRC_DDP);
dtree_printd(DTREE_PRINT_GENERIC,"OpenDDR device data: '%s'\n",fpath);
if(!(f=fopen(fpath,"r")))
{
fprintf(stderr,"OpenDDR: cannot open '%s'\n",fpath);
if(!i)
goto dexit;
}
while((ret=openddr_read_device_raw(f,h,&dev)))
{
if(ret==-1)
goto dexit;
dcount++;
}
fclose(f);
}
dtree_printd(DTREE_PRINT_INITDTREE,"OpenDDR device data raw count: %d\n",dcount);
dtree_printd(DTREE_PRINT_INITDTREE,"Open DDR raw dtree stats: nodes: %zu slabs: %zu mem: %zu bytes strings: %zu(%zu,%zu)\n",
dev.node_count,dev.slab_count,dev.size,dev.dc_count,dev.dc_slab_count,dev.dc_slab_pos);
//build main pattern tree
for(i=0;i<2;i++)
{
if(!i)
sprintf(fpath,"%s/%s",path,OPENDDR_RSRC_BD);
else
sprintf(fpath,"%s/%s",path,OPENDDR_RSRC_BDP);
dtree_printd(DTREE_PRINT_GENERIC,"OpenDDR builder data: '%s'\n",fpath);
if(!(f=fopen(fpath,"r")))
{
fprintf(stderr,"OpenDDR: cannot open '%s'\n",fpath);
if(!i)
goto dexit;
}
while((ret=openddr_read_pattern(f,h,&dev,&flags)))
{
if(ret==-1)
goto dexit;
pcount++;
}
fclose(f);
}
dtree_printd(DTREE_PRINT_INITDTREE,"OpenDDR pattern data raw count: %d\n",pcount);
dtree_free(&dev);
return pcount;
dexit:
if(f)
fclose(f);
dtree_free(&dev);
dtree_free(h);
dclass_init_index(di);
return -1;
}
//read a device from file, return it
static int openddr_read_device_raw(FILE *f,dtree_dt_index *h,dtree_dt_index *dev)
{
int i;
int ret=0;
char buf[1048];
char temp[DTREE_DATA_BUFLEN];
dclass_keyvalue *kvd=NULL;
dtree_dt_add_entry entry;
if(!f)
return 0;
while((buf[sizeof(buf)-2]='\n') && fgets(buf,sizeof(buf),f))
{
//overflow
if(buf[sizeof(buf)-2]!='\n' && buf[sizeof(buf)-2])
{
dtree_printd(DTREE_PRINT_INITDTREE,"OPENDDR LOAD: overflow detected\n");
do
{
buf[sizeof(buf)-2]='\n';
if(!fgets(buf,sizeof(buf),f))
break;
}
while(buf[sizeof(buf)-2]!='\n' && buf[sizeof(buf)-2]);
continue;
}
if(strstr(buf,"<device "))
{
openddr_get_attr(buf,"id",NULL,NULL,temp);
kvd=(dclass_keyvalue*)dtree_get(dev,temp,0);
if(kvd)
dtree_printd(DTREE_PRINT_INITDTREE,"OpenDDR device overwrite found: '%s'\n",temp);
else
kvd=(dclass_keyvalue*)dtree_alloc_mem(h,sizeof(dclass_keyvalue));
if(!kvd)
return -1;
kvd->size=sizeof(openddr_key_array)/sizeof(*openddr_key_array);
kvd->keys=openddr_key_array;
kvd->values=(const char**)dtree_alloc_mem(h,sizeof(openddr_key_array));
if(!kvd->values)
return -1;
kvd->id=dtree_alloc_string(h,temp,strlen(temp));
openddr_get_attr(buf,"parentId",h,dclass_get_value_pos(kvd,"parentId"),NULL);
ret=1;
if(strstr(buf,"/>"))
break;
}
else if(strstr(buf,"</device>"))
break;
else if(kvd && strstr(buf,"<property "))
{
openddr_get_attr(buf,"name",NULL,NULL,temp);
openddr_get_attr(buf,"value",h,dclass_get_value_pos(kvd,temp),NULL);
}
}
if(kvd)
{
dtree_printd(DTREE_PRINT_INITDTREE,"OpenDDR id(%p): '%s'(%p)\nAttributes => ",kvd,kvd->id,kvd->id);
for(i=0;i<kvd->size;i++)
dtree_printd(DTREE_PRINT_INITDTREE,"%s: '%s' ",kvd->keys[i],kvd->values[i]);
dtree_printd(DTREE_PRINT_INITDTREE,"\n");
memset(&entry,0,sizeof(dtree_dt_add_entry));
entry.data=kvd;
//add to device tree
if(dtree_add_entry(dev,kvd->id,&entry)<0)
return -1;
}
return ret;
}
//read a pattern from file, insert it
static int openddr_read_pattern(FILE *f,dtree_dt_index *h,dtree_dt_index *dev,dtree_flag_f *flags)
{
int ret=0;
char buf[1048];
char pattern[DTREE_DATA_BUFLEN];
char id[DTREE_DATA_BUFLEN];
dclass_keyvalue *der=NULL;
dclass_keyvalue *chain=NULL;
const dtree_dt_node *base;
#if OPENDDR_BLKBRY_FIX
dtree_flag_f bb_flag;
dclass_keyvalue *bb_chain;
#endif
if(!f)
return 0;
while((buf[sizeof(buf)-2]='\n') && fgets(buf,sizeof(buf),f))
{
//overflow
if(buf[sizeof(buf)-2]!='\n' && buf[sizeof(buf)-2])
{
dtree_printd(DTREE_PRINT_INITDTREE,"OPENDDR LOAD: overflow detected\n");
do
{
buf[sizeof(buf)-2]='\n';
if(!fgets(buf,sizeof(buf),f))
break;
}
while(buf[sizeof(buf)-2]!='\n' && buf[sizeof(buf)-2]);
continue;
}
if(!h->comment && strstr(buf,"<ver>"))
{
openddr_get_value(buf,"ver",NULL,NULL,pattern);
sprintf(buf,OPENDDR_COMMENT,pattern);
h->comment=dtree_alloc_mem(h,(strlen(buf)+1)*sizeof(char));
if(h->comment)
strncpy(h->comment,buf,strlen(buf));
}
else if(strstr(buf,"<device "))
{
openddr_get_attr(buf,"id",NULL,NULL,id);
ret=1;
//get device from device tree
der=(dclass_keyvalue*)dtree_get(dev,id,0);
}
else if(strstr(buf,"<builder "))
{
if(strstr(buf,"TwoStepDeviceBuilder"))
*flags=DTREE_DT_FLAG_CHAIN;
else if(strstr(buf,"SimpleDeviceBuilder"))
*flags=DTREE_DT_FLAG_WEAK;
}
else if(strstr(buf,"</builder>") && *flags)
*flags=0;
else if(strstr(buf,"</device>"))
break;
else if(der && strstr(buf,"<value>"))
{
openddr_get_value(buf,"value",NULL,NULL,pattern);
dtree_printd(DTREE_PRINT_INITDTREE,"OpenDDR pattern: '%s' id: '%s' flags: %d (%p)\n",
pattern,der->id,*flags,der);
if(!chain && *flags & DTREE_DT_FLAG_CHAIN)
{
dtree_printd(DTREE_PRINT_INITDTREE,"OpenDDR CHAIN PATTERN: '%s' id: '%s'\n",pattern,der->id);
chain=(dclass_keyvalue*)dtree_get(h,pattern,DTREE_DT_FLAG_BCHAIN);
//TODO need to iterate thru regex and add all possible chain values
if(!chain)
{
dtree_printd(DTREE_PRINT_INITDTREE,"OpenDDR ALT CHAIN PATTERN: '%s' id: '%s'\n",openddr_unregex(pattern,id),der->id);
base=dtree_get_node(h,openddr_unregex(pattern,id),0,0);
if(base && (base=dtree_get_flag(h,base,DTREE_DT_FLAG_BCHAIN,0)))
chain=base->payload;
}
if(chain)
{
dtree_printd(DTREE_PRINT_INITDTREE,"OpenDDR FOUND DUP CHAIN PATTERN: '%s' id: '%s' (%s)\n",pattern,der->id,chain->id);
continue;
}
}
#if OPENDDR_BLKBRY_FIX
bb_flag=*flags;
bb_chain=chain;
if(!strncasecmp(der->id,"blackberry",10) && chain && (*flags & DTREE_DT_FLAG_CHAIN))
{
*flags=0;
chain=NULL;
openddr_convert_bb(pattern);
}
#endif
if(openddr_add_device_pattern(h,dev,der,chain,pattern,*flags)<0)
return -1;
#if OPENDDR_BLKBRY_FIX
*flags=bb_flag;
chain=bb_chain;
#endif
if(*flags & DTREE_DT_FLAG_CHAIN)
chain=der;
}
}
return ret;
}
//adds a device pattern to the root tree
static int openddr_add_device_pattern(dtree_dt_index *h,dtree_dt_index *dev,dclass_keyvalue *der,dclass_keyvalue *chain,char *pattern,dtree_flag_f flags)
{
int i;
dtree_dt_add_entry entry;
//chain pattern
if((flags & DTREE_DT_FLAG_CHAIN))
{
if(!chain)
flags |= DTREE_DT_FLAG_BCHAIN;
dtree_printd(DTREE_PRINT_INITDTREE,"OpenDDR CHAIN PATTERN: '%s' current: '%s' prev: '%s' flag: %u\n",
pattern,der->id,chain?chain->id:"HEAD",flags);
}
for(i=0;i<der->size;i++)
{
if(!der->values[i])
der->values[i]=openddr_copy_string(dev,dclass_get_kvalue(der,"parentId"),der->keys[i]);
}
memset(&entry,0,sizeof(dtree_dt_add_entry));
entry.data=der;
entry.flags=flags;
entry.param=chain;
return dtree_add_entry(h,pattern,&entry);
}
//alloc a device kvd
static int openddr_alloc_kvd(dtree_dt_index *h,dtree_dt_index *dev,char *id)
{
dclass_keyvalue *kvd;
dtree_dt_add_entry entry;
kvd=(dclass_keyvalue*)dtree_alloc_mem(h,sizeof(dclass_keyvalue));
if(!kvd)
return 0;
kvd->id=dtree_alloc_string(h,id,strlen(id));
memset(&entry,0,sizeof(dtree_dt_add_entry));
entry.data=kvd;
dtree_add_entry(dev,kvd->id,&entry);
return 1;
}
//copy string from parent
static const char *openddr_copy_string(dtree_dt_index *h,const char *parent,const char *key)
{
dclass_keyvalue *p;
const char *ret;
p=(dclass_keyvalue*)dtree_get(h,parent,0);
if(!p)
return NULL;
ret=dclass_get_kvalue(p,key);
if(!ret)
return openddr_copy_string(h,dclass_get_kvalue(p,"parentId"),key);
return ret;
}
//looks for attr in buf and copies it to dest
static char *openddr_get_attr(const char *buf,const char *attr,dtree_dt_index *h,char **vbuf,char *retbuf)
{
return openddr_get_str(buf,attr,h,vbuf,retbuf,"=",'\"');
}
//looks for value in buf and copies it to dest
static char *openddr_get_value(const char *buf,const char *attr,dtree_dt_index *h,char **vbuf,char *retbuf)
{
return openddr_get_str(buf,attr,h,vbuf,retbuf,">",'<');
}
//strcasestr
static const char *_alt_strcasestr(const char *haystack,const char *needle)
{
const char *p;
const char *startn=0;
const char *np=0;
for (p=haystack;*p;p++)
{
if(np)
{
if(toupper((unsigned char)*p)==toupper((unsigned char)*np))
{
if(!*++np)
return startn;
}
else
np = 0;
}
else if(toupper((unsigned char)*p)==toupper((unsigned char)*needle))
{
np = needle + 1;
startn = p;
}
}
return 0;
}
//a generic str parser
static char *openddr_get_str(const char *buf,const char *attr,dtree_dt_index *h,char **vbuf,char *retbuf,char *suffix,char term)
{
int i;
const char *s;
const char *e;
char *ret=NULL;
char temp[DTREE_DATA_BUFLEN];
if(h && !vbuf)
return NULL;
i=sizeof(temp)-2-strlen(suffix);
strncpy(temp,attr,i);
temp[i+1]='\0';
strcat(temp,suffix);
s=_alt_strcasestr(buf,temp);
if(s)
{
s+=strlen(temp);
if(*s==term)
s++;
e=s;
while(*e!=term)
{
//non quoted attribute
if(*suffix=='=' && *e<=' ' && *(s-1)!='\"')
break;
e++;
}
i=e-s;
if(i>=DTREE_DATA_BUFLEN)
i=DTREE_DATA_BUFLEN-1;
if(h)
{
ret=dtree_alloc_string(h,s,i);
*vbuf=ret;
}
if(retbuf)
{
ret=strncpy(retbuf,s,i);
retbuf[i]='\0';
}
}
return ret;
}
//removes regex
static char *openddr_unregex(const char *s,char *dest)
{
int h=0;
char *d=dest;
while(*s)
{
if(*s==DTREE_PATTERN_SET_S)
{
*d++=*++s;
h=1;
continue;
}
else if(*s==DTREE_PATTERN_SET_E)
{
s++;
h=0;
continue;
}
else if(*s==DTREE_PATTERN_OPTIONAL || *s==DTREE_PATTERN_BEGIN || *s==DTREE_PATTERN_END ||
*s==DTREE_PATTERN_GROUP_S || *s==DTREE_PATTERN_GROUP_E || *s==DTREE_PATTERN_ESCAPE)
{
s++;
continue;
}
if(!h)
*d++=*s++;
else
s++;
}
*d='\0';
return dest;
}
#if OPENDDR_BLKBRY_FIX
//blackberry pattern fix
static void openddr_convert_bb(char *pattern)
{
char *orig=pattern;
char new[DTREE_DATA_BUFLEN];
*new='\0';
if(strncasecmp(orig,"blackberry",10))
return;
strcat(new,"blackberry.?");
orig+=10;
if(*orig==' ')
orig++;
if(strlen(orig)>8)
return;
strcat(new,orig);
strcat(new,".?");
strncpy(pattern,new,DTREE_DATA_BUFLEN-1);
pattern[DTREE_DATA_BUFLEN-1]='\0';
}
#endif