2 * MP3/MPlayer plugin to VDR (C++)
4 * (C) 2001-2009 Stefan Huelswitt <s.huelswitt@gmx.de>
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.
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.
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
29 #include <sys/types.h>
34 #include <vdr/tools.h>
40 // ----------------------------------------------------------------
42 const char *mountscript = "mount.sh";
44 char *Quote(const char *str)
46 char *nstr=MALLOC(char,strlen(str)*2);
51 case '\\': // backslash
52 case '\"': // double quote
53 case '`': // back tick
62 char *AddPath(const char *dir, const char *filename)
64 char *name=aprintf("%s/%s",dir,filename);
68 bool CheckVDRVersion(int Version, int Major, int Minor, const char *text)
70 static char vv[] = VDRVERSION;
71 int version, major, minor;
72 if(sscanf(vv,"%d.%d.%d",&version,&major,&minor)==3) {
74 (version==Version && major<Major) ||
75 (version==Version && major==Major && minor<Minor)) {
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);
83 else esyslog("ERROR: cannot parse VDR version string '%s'",vv);
87 char *aprintf(const char *fmt, ...)
92 if(vasprintf(&str,fmt,ap)<0);
97 // -- cScanDir --------------------------------------------------------------
99 bool cScanDir::ScanDir(cFileSource *src, const char *subdir, eScanType type, const char * const *spec, const char * const *excl, bool recursiv)
103 char *dir=aprintf(subdir ? "%s/%s":"%s",src->BaseDir(),subdir);
107 while((e=readdir64(d))) {
108 if(!strcmp(e->d_name,".") || !strcmp(e->d_name,"..")) continue;
110 if(!(f=AddPath(dir,e->d_name))) continue;
112 if(stat64(f,&st)<0) {
113 esyslog("ERROR: stat(1) %s: %s",f,strerror(errno));
116 if(S_ISLNK(st.st_mode)) {
117 char *of=f; f=ReadLink(of); free(of);
119 if(stat64(f,&st)<0) {
120 esyslog("ERROR: stat(2) %s: %s",f,strerror(errno));
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);
132 if(type!=stDir) continue;
134 if(S_ISREG(st.st_mode)) {
135 if(type!=stFile) continue;
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));
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));
155 DoItem(src,subdir,e->d_name);
160 esyslog("ERROR: opendir %s: %s",dir,strerror(errno));
167 // -- cFileObj --------------------------------------------------------------
169 cFileObj::cFileObj(cFileSource *Source, const char *Subdir, const char *Name, const eObjType Type)
173 subdir=Subdir ? strdup(Subdir):0;
174 name=Name ? strdup(Name):0;
179 cFileObj::cFileObj(const cFileObj *obj)
183 subdir=obj->subdir ? strdup(obj->subdir):0;
184 name=obj->name ? strdup(obj->name):0;
189 cFileObj::~cFileObj()
197 int cFileObj::Compare(const cListObject &ListObject) const
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;
208 return strcasecmp(path,obj->path);
211 void cFileObj::SplitAndSet(const char *Path)
213 free(subdir); subdir=0;
216 int l=strlen(source->BaseDir());
217 if(!strncasecmp(Path,source->BaseDir(),l)) p+=l+1;
219 l=strlen(source->RealBaseDir());
220 if(!strncasecmp(Path,source->RealBaseDir(),l)) p+=l+1;
222 char buff[strlen(Path)+5];
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));
233 if(!strncasecmp(real,source->RealBaseDir(),l))
235 const char *r=index(p,'/');
237 esyslog("ERROR: can't find source basedir in '%s'. Outside source?",Path);
241 strn0cpy(buff,Path,r-Path+1);
248 const char *s=rindex(p,'/');
251 subdir=MALLOC(char,l);
252 if(subdir) strn0cpy(subdir,p,l);
259 void cFileObj::SetName(const char *Name)
262 name=Name ? strdup(Name):0;
266 void cFileObj::Set(void)
269 path=aprintf(subdir ? "%2$s/%1$s":"%s",name,subdir);
270 free(fpath); fpath=0;
271 MakeFullName(&fpath,name);
274 void cFileObj::MakeFullName(char **fp, const char *Name)
276 *fp=aprintf(subdir ? "%1$s/%3$s/%2$s":"%s/%s",source->BaseDir(),Name,subdir);
279 bool cFileObj::GuessType(void)
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;
291 bool cFileObj::Exists(void)
295 if(!stat64(fpath,&ds) &&
296 S_ISREG(ds.st_mode) &&
297 !access(fpath,R_OK)) return true;
302 bool cFileObj::TestName(const char *newName)
307 MakeFullName(&fname,newName);
308 if(access(fname,F_OK)==0) r=true;
314 bool cFileObj::Rename(const char *newName)
319 MakeFullName(&fname,newName);
320 if(access(fname,F_OK) && (!rename(fpath,fname))) {
329 bool cFileObj::Create(const char *newName)
334 MakeFullName(&fname,newName);
336 if(access(fname,F_OK) && (newf=fopen(fname,"w"))) {
346 bool cFileObj::Delete(void)
348 if(type==otFile && !unlink(fpath)) return true;
352 // -- cDirList --------------------------------------------------------------
354 bool cDirList::Load(cFileSource *src, const char *subdir, const char * const *excl)
356 static const char *excl_s[] = { ".*",0 };
360 if(subdir) Add(new cFileObj(src,subdir,"..",otParent));
362 if(ScanDir(src,subdir,stDir,0,0,false)) {
364 if(!excl) excl=excl_s;
365 if(ScanDir(src,subdir,stFile,src->Include(),excl,false)) res=true;
371 void cDirList::DoItem(cFileSource *src, const char *subdir, const char *name)
373 Add(new cFileObj(src,subdir,name,otype));
376 // -- cFileSource --------------------------------------------------------------
378 cFileSource::cFileSource(void)
380 browsedir=browseparent=0;
381 basedir=realbasedir=description=0; useCount=0;
383 include=0; incCount=0;
386 cFileSource::cFileSource(const char *Basedir, const char *Description, const bool NeedsMount, const char *Include)
388 browsedir=browseparent=0;
389 basedir=realbasedir=description=0; useCount=0;
390 include=0; incCount=0;
391 Set(Basedir,Description,NeedsMount,Include);
394 cFileSource::~cFileSource()
400 void cFileSource::Clear(void)
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;
409 void cFileSource::Set(const char *Basedir, const char *Description, const bool NeedsMount, const char *Include)
412 basedir=strdup(Basedir);
413 description=strdup(Description);
416 char *s=index(Include,'/');
417 int l=s ? s-Include : strlen(Include);
419 char **s=(char **)realloc(include,(incCount+2)*sizeof(char *));
422 include[incCount]=strndup(Include,l);
427 Include+=l+(s ? 1:0);
432 printf("sources: filesource %s includes (count=%d):",basedir,incCount);
433 for(int i=0; i<incCount; i++) printf(" '%s'",include[i]);
437 printf("sources: filesource %s has no includes set\n",basedir);
439 needsmount=NeedsMount;
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); }
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;
452 strn0cpy(realbasedir,basedir,PATH_MAX);
456 void cFileSource::SetRemember(const char *dir, const char *parent)
459 if(dir) browsedir=strdup(dir);
460 if(parent) browseparent=strdup(parent);
463 void cFileSource::ClearRemember(void)
465 free(browsedir); browsedir=0;
466 free(browseparent); browseparent=0;
469 bool cFileSource::GetRemember(char * &dir, char * &parent)
473 if(browseparent) parent=strdup(browseparent);
474 dir=strdup(browsedir);
480 bool cFileSource::Parse(char *s)
482 char base[256], des[256], incl[256];
484 if((n=sscanf(s,"%255[^;];%255[^;];%d;%255[^;]",base,des,&needsmount,incl))>=3) {
485 char *base2=skipspace(stripspace(base));
487 while(l>0 && base2[l-1]=='/') {
488 esyslog("WARNING: removing trailing '/' from base %s",base2);
492 Set(base2,skipspace(stripspace(des)),needsmount!=0,n>3?skipspace(stripspace(incl)):0);
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); }
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); }
506 bool cFileSource::Action(eAction act)
508 static const char *str[] = { "mount","unmount","eject","status" };
510 char *cmd=aprintf("%s %s %s",mountscript,str[act],basedir);
511 bool res=(system(cmd)==0);
516 bool cFileSource::Mount(void)
519 if(needsmount && (res=Action(acMount))) ClearRemember();
523 bool cFileSource::Unmount(void)
527 if(!useCount && (res=Action(acUnmount))) ClearRemember();
532 bool cFileSource::Eject(void)
536 if(!useCount && (res=Action(acEject))) ClearRemember();
541 bool cFileSource::Status(void)
543 if(needsmount) return Action(acStatus);
547 // -- cFileSources --------------------------------------------------------------
549 bool cFileSources::Load(const char *filename, bool dummy)
551 if(cConfig<cFileSource>::Load(filename,true)) {
558 cFileSource *cFileSources::FindSource(const char *filename)
560 cFileSource *src=First();
562 if(startswith(filename,src->RealBaseDir())) return src;