data.c
author nathan
Mon, 17 Aug 2009 20:56:48 +0800
branchtrunk
changeset 29 640ce9201139
parent 22 93aaf15c145a
child 32 cea1b4f741be
permissions -rw-r--r--
fix gcc 4.x warnings
     1 /*
     2  * MP3/MPlayer plugin to VDR (C++)
     3  *
     4  * (C) 2001-2009 Stefan Huelswitt <s.huelswitt@gmx.de>
     5  *
     6  * This code is free software; you can redistribute it and/or
     7  * modify it under the terms of the GNU General Public License
     8  * as published by the Free Software Foundation; either version 2
     9  * of the License, or (at your option) any later version.
    10  *
    11  * This code is distributed in the hope that it will be useful,
    12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    14  * GNU General Public License for more details.
    15  *
    16  * You should have received a copy of the GNU General Public License
    17  * along with this program; if not, write to the Free Software
    18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
    19  * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
    20  */
    21 
    22 #include <ctype.h>
    23 #include <dirent.h>
    24 #include <stdlib.h>
    25 #include <stdio.h>
    26 #include <stdarg.h>
    27 #include <errno.h>
    28 #include <sys/stat.h>
    29 #include <sys/types.h>
    30 #include <unistd.h>
    31 #include <dirent.h>
    32 #include <fnmatch.h>
    33 
    34 #include <vdr/tools.h>
    35 
    36 #include "common.h"
    37 #include "data.h"
    38 #include "data-src.h"
    39 
    40 // ----------------------------------------------------------------
    41 
    42 const char *mountscript = "mount.sh";
    43 
    44 char *Quote(const char *str)
    45 {
    46   char *nstr=MALLOC(char,strlen(str)*2);
    47   char *p=nstr;
    48   while(*str) {
    49     switch(*str) {
    50       case '$':  // dollar
    51       case '\\': // backslash
    52       case '\"': // double quote
    53       case '`':  // back tick
    54                  *p++='\\'; break;
    55       }
    56     *p++=*str++;
    57     }
    58   *p=0;
    59   return nstr;
    60 }
    61 
    62 char *AddPath(const char *dir, const char *filename)
    63 {
    64   char *name=aprintf("%s/%s",dir,filename);
    65   return name;
    66 }
    67 
    68 bool CheckVDRVersion(int Version, int Major, int Minor, const char *text)
    69 {
    70   static char vv[] = VDRVERSION;
    71   int version, major, minor;
    72   if(sscanf(vv,"%d.%d.%d",&version,&major,&minor)==3) {
    73     if(version<Version ||
    74        (version==Version && major<Major) ||
    75        (version==Version && major==Major && minor<Minor)) {
    76       if(text) {
    77         esyslog("ERROR: %s plugin needs at least VDR version %d.%d.%d",text,Version,Major,Minor);
    78         fprintf(stderr,"%s plugin needs at least VDR version %d.%d.%d\n",text,Version,Major,Minor);
    79         }
    80       return false;
    81       }
    82     }
    83   else esyslog("ERROR: cannot parse VDR version string '%s'",vv);
    84   return true;
    85 }
    86 
    87 char *aprintf(const char *fmt, ...)
    88 {
    89   va_list ap;
    90   va_start(ap,fmt);
    91   char *str=0;
    92   if(vasprintf(&str,fmt,ap)<0);
    93   va_end(ap);
    94   return str;
    95 }
    96 
    97 // -- cScanDir --------------------------------------------------------------
    98 
    99 bool cScanDir::ScanDir(cFileSource *src, const char *subdir, eScanType type, const char * const *spec, const char * const *excl, bool recursiv)
   100 {
   101   bool res=true;
   102   char *f=0;
   103   char *dir=aprintf(subdir ? "%s/%s":"%s",src->BaseDir(),subdir);
   104   DIR *d=opendir(dir);
   105   if(d) {
   106     struct dirent64 *e;
   107     while((e=readdir64(d))) {
   108       if(!strcmp(e->d_name,".") || !strcmp(e->d_name,"..")) continue;
   109       free(f);
   110       if(!(f=AddPath(dir,e->d_name))) continue;
   111       struct stat64 st;
   112       if(stat64(f,&st)<0) {
   113         esyslog("ERROR: stat(1) %s: %s",f,strerror(errno));
   114         continue;
   115         }
   116       if(S_ISLNK(st.st_mode)) {
   117         char *of=f; f=ReadLink(of); free(of);
   118         if(!f) continue;
   119         if(stat64(f,&st)<0) {
   120           esyslog("ERROR: stat(2) %s: %s",f,strerror(errno));
   121           continue;
   122           }
   123         }
   124       if(S_ISDIR(st.st_mode)) {
   125         if(type==stFile && recursiv) {
   126           char *s=aprintf(subdir ? "%2$s/%1$s":"%s",e->d_name,subdir);
   127           res=ScanDir(src,s,type,spec,excl,recursiv);
   128           free(s);
   129           if(!res) break;
   130           continue;
   131           }
   132         if(type!=stDir) continue;
   133         }
   134       if(S_ISREG(st.st_mode)) {
   135         if(type!=stFile) continue;
   136         if(spec) {
   137           bool ok=false;
   138           for(const char * const *m=spec; *m; m++) {
   139             int n=fnmatch(*m,e->d_name,FNM_CASEFOLD);
   140             if(n==0) { ok=true; break; }
   141             if(n!=FNM_NOMATCH) esyslog("ERROR: fnmatch(1) %s: %s",*m,strerror(errno));
   142             }
   143           if(!ok) continue;
   144           }
   145         if(excl) {
   146           bool ok=true;
   147           for(const char * const *m=excl; *m; m++) {
   148             int n=fnmatch(*m,e->d_name,FNM_CASEFOLD);
   149             if(n==0) { ok=false; break; }
   150             if(n!=FNM_NOMATCH) esyslog("ERROR: fnmatch(2) %s: %s",*m,strerror(errno));
   151             }
   152           if(!ok) continue;
   153           }
   154         }
   155       DoItem(src,subdir,e->d_name);
   156       }
   157     closedir(d);
   158     }
   159   else {
   160     esyslog("ERROR: opendir %s: %s",dir,strerror(errno));
   161     res=false;
   162     }
   163   free(dir); free(f);
   164   return res;
   165 }
   166 
   167 // -- cFileObj --------------------------------------------------------------
   168 
   169 cFileObj::cFileObj(cFileSource *Source, const char *Subdir, const char *Name, const eObjType Type)
   170 {
   171   path=fpath=0;
   172   source=Source;
   173   subdir=Subdir ? strdup(Subdir):0;
   174   name=Name ? strdup(Name):0;
   175   type=Type;
   176   Set();
   177 }
   178 
   179 cFileObj::cFileObj(const cFileObj *obj)
   180 {
   181   path=fpath=0;
   182   source=obj->source;
   183   subdir=obj->subdir ? strdup(obj->subdir):0;
   184   name=obj->name ? strdup(obj->name):0;
   185   type=obj->type;
   186   Set();
   187 }
   188 
   189 cFileObj::~cFileObj()
   190 {
   191   free(name);
   192   free(subdir);
   193   free(path);
   194   free(fpath);
   195 }
   196 
   197 int cFileObj::Compare(const cListObject &ListObject) const
   198 {
   199   cFileObj *obj=(cFileObj *)&ListObject;
   200   if(type==otParent) return obj->type==otParent ? 0:-1;
   201   if(obj->type==otParent) return 1;
   202   if(type==otBase) return obj->type==otBase ? 0:1;
   203   if(obj->type==otBase) return -1;
   204   if(type!=obj->type) {
   205     if(type==otFile) return 1;
   206     return -1;
   207     }
   208   return strcasecmp(path,obj->path);
   209 }
   210 
   211 void cFileObj::SplitAndSet(const char *Path)
   212 {
   213   free(subdir); subdir=0;
   214   const char *p=Path;
   215   if(Path[0]=='/') {
   216     int l=strlen(source->BaseDir());
   217     if(!strncasecmp(Path,source->BaseDir(),l)) p+=l+1;
   218     else {
   219       l=strlen(source->RealBaseDir());
   220       if(!strncasecmp(Path,source->RealBaseDir(),l)) p+=l+1;
   221       else {
   222         char buff[strlen(Path)+5];
   223         strcpy(buff,"/");
   224         p++;
   225         while(1) {
   226           char real[PATH_MAX+1];
   227           if(!realpath(buff,real)) {
   228             if(errno!=ENOENT && errno!=ENOTDIR)
   229               esyslog("ERROR: realpath: %s: %s",buff,strerror(errno));
   230             p=Path+1;
   231             break;
   232             }
   233           if(!strncasecmp(real,source->RealBaseDir(),l))
   234             break;
   235           const char *r=index(p,'/');
   236           if(!r) {
   237             esyslog("ERROR: can't find source basedir in '%s'. Outside source?",Path);
   238             p=Path+1;
   239             break;
   240             }
   241           strn0cpy(buff,Path,r-Path+1);
   242           p=r+1;
   243           }
   244         }
   245       }
   246     }
   247 
   248   const char *s=rindex(p,'/');
   249   if(s) {
   250     const int l=s-p+1;
   251     subdir=MALLOC(char,l);
   252     if(subdir) strn0cpy(subdir,p,l);
   253     SetName(s+1);
   254     }
   255   else
   256     SetName(p);
   257 }
   258 
   259 void cFileObj::SetName(const char *Name)
   260 {
   261   free(name);
   262   name=Name ? strdup(Name):0;
   263   Set();
   264 }
   265 
   266 void cFileObj::Set(void)
   267 {
   268   free(path);
   269   path=aprintf(subdir ? "%2$s/%1$s":"%s",name,subdir);
   270   free(fpath); fpath=0;
   271   MakeFullName(&fpath,name);
   272 }
   273 
   274 void cFileObj::MakeFullName(char **fp, const char *Name)
   275 {
   276   *fp=aprintf(subdir ? "%1$s/%3$s/%2$s":"%s/%s",source->BaseDir(),Name,subdir);
   277 }
   278 
   279 bool cFileObj::GuessType(void)
   280 {
   281   struct stat64 ds;
   282   if(!stat64(fpath,&ds)) {
   283     if(S_ISREG(ds.st_mode))      type=otFile;
   284     else if(S_ISDIR(ds.st_mode)) type=subdir ? otDir:otBase;
   285     else return false;
   286     return true;
   287     }
   288   return false;
   289 }
   290 
   291 bool cFileObj::Exists(void)
   292 {
   293   if(type==otFile) {
   294     struct stat64 ds;
   295     if(!stat64(fpath,&ds) &&
   296        S_ISREG(ds.st_mode) &&
   297        !access(fpath,R_OK)) return true;
   298     }
   299   return false;
   300 }
   301 
   302 bool cFileObj::TestName(const char *newName)
   303 {
   304   bool r=false;
   305   if(type==otFile) {
   306     char *fname;
   307     MakeFullName(&fname,newName);
   308     if(access(fname,F_OK)==0) r=true;
   309     free(fname);
   310     }
   311   return r;
   312 }
   313 
   314 bool cFileObj::Rename(const char *newName)
   315 {
   316   bool r=false;
   317   if(type==otFile) {
   318     char *fname;
   319     MakeFullName(&fname,newName);
   320     if(access(fname,F_OK) && (!rename(fpath,fname))) {
   321       SetName(newName);
   322       r=true;
   323       }
   324     free(fname);
   325     }
   326   return r;
   327 }
   328 
   329 bool cFileObj::Create(const char *newName)
   330 {
   331   bool r=false;
   332   if(type==otFile) {
   333     char *fname;
   334     MakeFullName(&fname,newName);
   335     FILE *newf;
   336     if(access(fname,F_OK) && (newf=fopen(fname,"w"))) {
   337       fclose(newf);
   338       SetName(newName);
   339       r=true;
   340       }
   341     free(fname);
   342     }
   343   return r;
   344 }
   345 
   346 bool cFileObj::Delete(void)
   347 {
   348   if(type==otFile && !unlink(fpath)) return true;
   349   return false;
   350 }
   351 
   352 // -- cDirList --------------------------------------------------------------
   353 
   354 bool cDirList::Load(cFileSource *src, const char *subdir, const char * const *excl)
   355 {
   356   static const char *excl_s[] = { ".*",0 };
   357 
   358   bool res=false;
   359   Clear();
   360   if(subdir) Add(new cFileObj(src,subdir,"..",otParent));
   361   otype=otDir;
   362   if(ScanDir(src,subdir,stDir,0,0,false)) {
   363     otype=otFile;
   364     if(!excl) excl=excl_s;
   365     if(ScanDir(src,subdir,stFile,src->Include(),excl,false)) res=true;
   366     }
   367   Sort();
   368   return res;
   369 }
   370 
   371 void cDirList::DoItem(cFileSource *src, const char *subdir, const char *name)
   372 {
   373   Add(new cFileObj(src,subdir,name,otype));
   374 }
   375 
   376 // -- cFileSource --------------------------------------------------------------
   377 
   378 cFileSource::cFileSource(void)
   379 {
   380   browsedir=browseparent=0;
   381   basedir=realbasedir=description=0; useCount=0;
   382   needsmount=false;
   383   include=0; incCount=0;
   384 }
   385 
   386 cFileSource::cFileSource(const char *Basedir, const char *Description, const bool NeedsMount, const char *Include)
   387 {
   388   browsedir=browseparent=0;
   389   basedir=realbasedir=description=0; useCount=0;
   390   include=0; incCount=0;
   391   Set(Basedir,Description,NeedsMount,Include);
   392 }
   393 
   394 cFileSource::~cFileSource()
   395 {
   396   ClearRemember();
   397   Clear();
   398 }
   399 
   400 void cFileSource::Clear(void)
   401 {
   402   free(basedir); basedir=0;
   403   free(realbasedir); realbasedir=0;
   404   free(description); description=0;
   405   for(int i=0; i<incCount; i++) free(include[i]);
   406   free(include); include=0; incCount=0;
   407 }
   408 
   409 void cFileSource::Set(const char *Basedir, const char *Description, const bool NeedsMount, const char *Include)
   410 {
   411   Clear();
   412   basedir=strdup(Basedir);
   413   description=strdup(Description);
   414   if(Include) {
   415     do {
   416       char *s=index(Include,'/');
   417       int l=s ? s-Include : strlen(Include);
   418       if(l) {
   419         char **s=(char **)realloc(include,(incCount+2)*sizeof(char *));
   420         if(s) {
   421           include=s;
   422           include[incCount]=strndup(Include,l);
   423           incCount++;
   424           include[incCount]=0;
   425           }
   426         }
   427       Include+=l+(s ? 1:0);
   428       } while(*Include>0);
   429     }
   430 #ifdef DEBUG
   431   if(include) {
   432     printf("sources: filesource %s includes (count=%d):",basedir,incCount);
   433     for(int i=0; i<incCount; i++) printf(" '%s'",include[i]);
   434     printf("\n");
   435     }
   436   else
   437     printf("sources: filesource %s has no includes set\n",basedir);
   438 #endif
   439   needsmount=NeedsMount;
   440 
   441   realbasedir=MALLOC(char,PATH_MAX+1);
   442   if(realpath(basedir,realbasedir)) {
   443     if(strcmp(basedir,realbasedir)) { esyslog("WARNING: source base %s expands to %s",basedir,realbasedir); }
   444     }
   445   else {
   446     switch(errno) {
   447       case EACCES:  esyslog("ERROR: source base %s permission denied",basedir); break;
   448       case ENOENT:  esyslog("ERROR: source base %s not found",basedir); break;
   449       case ENOTDIR: esyslog("ERROR: source base %s has invalid path",basedir); break;
   450       default:      esyslog("ERROR: source base %s realpath: %s",basedir,strerror(errno)); break;
   451       }
   452     strn0cpy(realbasedir,basedir,PATH_MAX);
   453     }
   454 }
   455 
   456 void cFileSource::SetRemember(const char *dir, const char *parent)
   457 {
   458   ClearRemember();
   459   if(dir) browsedir=strdup(dir);
   460   if(parent) browseparent=strdup(parent);
   461 }
   462 
   463 void cFileSource::ClearRemember(void)
   464 {
   465   free(browsedir); browsedir=0;
   466   free(browseparent); browseparent=0;
   467 }
   468 
   469 bool cFileSource::GetRemember(char * &dir, char * &parent)
   470 {
   471   dir=parent=0;
   472   if(browsedir) {
   473     if(browseparent) parent=strdup(browseparent);
   474     dir=strdup(browsedir);
   475     return true;
   476     }
   477   return false;
   478 }
   479 
   480 bool cFileSource::Parse(char *s)
   481 {
   482   char base[256], des[256], incl[256];
   483   int needsmount, n;
   484   if((n=sscanf(s,"%255[^;];%255[^;];%d;%255[^;]",base,des,&needsmount,incl))>=3) {
   485     char *base2=skipspace(stripspace(base));
   486     int l=strlen(base2);
   487     while(l>0 && base2[l-1]=='/') {
   488       esyslog("WARNING: removing trailing '/' from base %s",base2);
   489       base2[l-1]=0;
   490       l--;
   491       }
   492     Set(base2,skipspace(stripspace(des)),needsmount!=0,n>3?skipspace(stripspace(incl)):0);
   493 
   494     // do some checking of the basedir and issue a warning if apropriate
   495     if(access(realbasedir,R_OK)) { esyslog("WARNING: source base %s not found/permission denied",realbasedir); }
   496     else {
   497       struct stat64 ds;
   498       if(lstat64(realbasedir,&ds)) { esyslog("WARNING: can't stat source base %s",realbasedir); }
   499       else if(!S_ISDIR(ds.st_mode)) { esyslog("WARNING: source base %s is not a directory",realbasedir); }
   500       }
   501     return true;
   502     }
   503   return false;
   504 }
   505 
   506 bool cFileSource::Action(eAction act)
   507 {
   508   static const char *str[] = { "mount","unmount","eject","status" };
   509   
   510   char *cmd=aprintf("%s %s %s",mountscript,str[act],basedir);
   511   bool res=(system(cmd)==0);
   512   free(cmd);
   513   return res;
   514 }
   515 
   516 bool cFileSource::Mount(void)
   517 {
   518   bool res=false;
   519   if(needsmount && (res=Action(acMount))) ClearRemember();
   520   return res;
   521 }
   522 
   523 bool cFileSource::Unmount(void)
   524 {
   525   bool res=false;
   526   if(needsmount) {
   527     if(!useCount && (res=Action(acUnmount))) ClearRemember();
   528     }
   529   return res;
   530 }
   531 
   532 bool cFileSource::Eject(void)
   533 {
   534   bool res=false;
   535   if(needsmount) {
   536     if(!useCount && (res=Action(acEject))) ClearRemember();
   537     }
   538   return res;
   539 }
   540 
   541 bool cFileSource::Status(void)
   542 {
   543   if(needsmount) return Action(acStatus);
   544   return true;
   545 }
   546 
   547 // -- cFileSources --------------------------------------------------------------
   548 
   549 bool cFileSources::Load(const char *filename, bool dummy)
   550 {
   551   if(cConfig<cFileSource>::Load(filename,true)) {
   552     SetSource(First());
   553     return true;
   554     }
   555   return false;
   556 }
   557 
   558 cFileSource *cFileSources::FindSource(const char *filename)
   559 {
   560   cFileSource *src=First();
   561   while(src) {
   562     if(startswith(filename,src->RealBaseDir())) return src;
   563     src=Next(src);
   564     }
   565   return 0;
   566 }