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 ScanDir(src,s,type,spec,excl,recursiv);
131 if(type!=stDir) continue;
133 if(S_ISREG(st.st_mode)) {
134 if(type!=stFile) continue;
137 for(const char * const *m=spec; *m; m++) {
138 int n=fnmatch(*m,e->d_name,FNM_CASEFOLD);
139 if(n==0) { ok=true; break; }
140 if(n!=FNM_NOMATCH) esyslog("ERROR: fnmatch(1) %s: %s",*m,strerror(errno));
146 for(const char * const *m=excl; *m; m++) {
147 int n=fnmatch(*m,e->d_name,FNM_CASEFOLD);
148 if(n==0) { ok=false; break; }
149 if(n!=FNM_NOMATCH) esyslog("ERROR: fnmatch(2) %s: %s",*m,strerror(errno));
154 DoItem(src,subdir,e->d_name);
159 esyslog("ERROR: opendir %s: %s",dir,strerror(errno));
166 // -- cFileObj --------------------------------------------------------------
168 cFileObj::cFileObj(cFileSource *Source, const char *Subdir, const char *Name, const eObjType Type)
172 subdir=Subdir ? strdup(Subdir):0;
173 name=Name ? strdup(Name):0;
178 cFileObj::cFileObj(const cFileObj *obj)
182 subdir=obj->subdir ? strdup(obj->subdir):0;
183 name=obj->name ? strdup(obj->name):0;
188 cFileObj::~cFileObj()
196 int cFileObj::Compare(const cListObject &ListObject) const
198 cFileObj *obj=(cFileObj *)&ListObject;
199 if(type==otParent) return obj->type==otParent ? 0:-1;
200 if(obj->type==otParent) return 1;
201 if(type==otBase) return obj->type==otBase ? 0:1;
202 if(obj->type==otBase) return -1;
203 if(type!=obj->type) {
204 if(type==otFile) return 1;
207 return strcasecmp(path,obj->path);
210 void cFileObj::SplitAndSet(const char *Path)
212 free(subdir); subdir=0;
215 int l=strlen(source->BaseDir());
216 if(!strncasecmp(Path,source->BaseDir(),l)) p+=l+1;
218 l=strlen(source->RealBaseDir());
219 if(!strncasecmp(Path,source->RealBaseDir(),l)) p+=l+1;
221 char buff[strlen(Path)+5];
225 char real[PATH_MAX+1];
226 if(!realpath(buff,real)) {
227 if(errno!=ENOENT && errno!=ENOTDIR)
228 esyslog("ERROR: realpath: %s: %s",buff,strerror(errno));
232 if(!strncasecmp(real,source->RealBaseDir(),l))
234 const char *r=index(p,'/');
236 esyslog("ERROR: can't find source basedir in '%s'. Outside source?",Path);
240 strn0cpy(buff,Path,r-Path+1);
247 const char *s=rindex(p,'/');
250 subdir=MALLOC(char,l);
251 if(subdir) strn0cpy(subdir,p,l);
258 void cFileObj::SetName(const char *Name)
261 name=Name ? strdup(Name):0;
265 void cFileObj::Set(void)
268 path=aprintf(subdir ? "%2$s/%1$s":"%s",name,subdir);
269 free(fpath); fpath=0;
270 MakeFullName(&fpath,name);
273 void cFileObj::MakeFullName(char **fp, const char *Name)
275 *fp=aprintf(subdir ? "%1$s/%3$s/%2$s":"%s/%s",source->BaseDir(),Name,subdir);
278 bool cFileObj::GuessType(void)
281 if(!stat64(fpath,&ds)) {
282 if(S_ISREG(ds.st_mode)) type=otFile;
283 else if(S_ISDIR(ds.st_mode)) type=subdir ? otDir:otBase;
290 bool cFileObj::Exists(void)
294 if(!stat64(fpath,&ds) &&
295 S_ISREG(ds.st_mode) &&
296 !access(fpath,R_OK)) return true;
301 bool cFileObj::TestName(const char *newName)
306 MakeFullName(&fname,newName);
307 if(access(fname,F_OK)==0) r=true;
313 bool cFileObj::Rename(const char *newName)
318 MakeFullName(&fname,newName);
319 if(access(fname,F_OK) && (!rename(fpath,fname))) {
328 bool cFileObj::Create(const char *newName)
333 MakeFullName(&fname,newName);
335 if(access(fname,F_OK) && (newf=fopen(fname,"w"))) {
345 bool cFileObj::Delete(void)
347 if(type==otFile && !unlink(fpath)) return true;
351 // -- cDirList --------------------------------------------------------------
353 bool cDirList::Load(cFileSource *src, const char *subdir, const char * const *excl)
355 static const char *excl_s[] = { ".*",0 };
359 if(subdir) Add(new cFileObj(src,subdir,"..",otParent));
361 if(ScanDir(src,subdir,stDir,0,0,false)) {
363 if(!excl) excl=excl_s;
364 if(ScanDir(src,subdir,stFile,src->Include(),excl,false)) res=true;
370 void cDirList::DoItem(cFileSource *src, const char *subdir, const char *name)
372 Add(new cFileObj(src,subdir,name,otype));
375 // -- cFileSource --------------------------------------------------------------
377 cFileSource::cFileSource(void)
379 browsedir=browseparent=0;
380 basedir=realbasedir=description=0; useCount=0;
382 include=0; incCount=0;
385 cFileSource::cFileSource(const char *Basedir, const char *Description, const bool NeedsMount, const char *Include)
387 browsedir=browseparent=0;
388 basedir=realbasedir=description=0; useCount=0;
389 include=0; incCount=0;
390 Set(Basedir,Description,NeedsMount,Include);
393 cFileSource::~cFileSource()
399 void cFileSource::Clear(void)
401 free(basedir); basedir=0;
402 free(realbasedir); realbasedir=0;
403 free(description); description=0;
404 for(int i=0; i<incCount; i++) free(include[i]);
405 free(include); include=0; incCount=0;
408 void cFileSource::Set(const char *Basedir, const char *Description, const bool NeedsMount, const char *Include)
411 basedir=strdup(Basedir);
412 description=strdup(Description);
415 char *s=index(Include,'/');
416 int l=s ? s-Include : strlen(Include);
418 char **s=(char **)realloc(include,(incCount+2)*sizeof(char *));
421 include[incCount]=strndup(Include,l);
426 Include+=l+(s ? 1:0);
431 printf("sources: filesource %s includes (count=%d):",basedir,incCount);
432 for(int i=0; i<incCount; i++) printf(" '%s'",include[i]);
436 printf("sources: filesource %s has no includes set\n",basedir);
438 needsmount=NeedsMount;
440 realbasedir=MALLOC(char,PATH_MAX+1);
441 if(realpath(basedir,realbasedir)) {
442 if(strcmp(basedir,realbasedir)) { esyslog("WARNING: source base %s expands to %s",basedir,realbasedir); }
446 case EACCES: esyslog("ERROR: source base %s permission denied",basedir); break;
447 case ENOENT: esyslog("ERROR: source base %s not found",basedir); break;
448 case ENOTDIR: esyslog("ERROR: source base %s has invalid path",basedir); break;
449 default: esyslog("ERROR: source base %s realpath: %s",basedir,strerror(errno)); break;
451 strn0cpy(realbasedir,basedir,PATH_MAX);
455 void cFileSource::SetRemember(const char *dir, const char *parent)
458 if(dir) browsedir=strdup(dir);
459 if(parent) browseparent=strdup(parent);
462 void cFileSource::ClearRemember(void)
464 free(browsedir); browsedir=0;
465 free(browseparent); browseparent=0;
468 bool cFileSource::GetRemember(char * &dir, char * &parent)
472 if(browseparent) parent=strdup(browseparent);
473 dir=strdup(browsedir);
479 bool cFileSource::Parse(char *s)
481 char base[256], des[256], incl[256];
483 if((n=sscanf(s,"%255[^;];%255[^;];%d;%255[^;]",base,des,&needsmount,incl))>=3) {
484 char *base2=skipspace(stripspace(base));
486 while(l>0 && base2[l-1]=='/') {
487 esyslog("WARNING: removing trailing '/' from base %s",base2);
491 Set(base2,skipspace(stripspace(des)),needsmount!=0,n>3?skipspace(stripspace(incl)):0);
493 // do some checking of the basedir and issue a warning if apropriate
494 if(access(realbasedir,R_OK)) { esyslog("WARNING: source base %s not found/permission denied",realbasedir); }
497 if(lstat64(realbasedir,&ds)) { esyslog("WARNING: can't stat source base %s",realbasedir); }
498 else if(!S_ISDIR(ds.st_mode)) { esyslog("WARNING: source base %s is not a directory",realbasedir); }
505 bool cFileSource::Action(eAction act)
507 static const char *str[] = { "mount","unmount","eject","status" };
509 char *cmd=aprintf("%s %s %s",mountscript,str[act],basedir);
510 bool res=(system(cmd)==0);
515 bool cFileSource::Mount(void)
518 if(needsmount && (res=Action(acMount))) ClearRemember();
522 bool cFileSource::Unmount(void)
526 if(!useCount && (res=Action(acUnmount))) ClearRemember();
531 bool cFileSource::Eject(void)
535 if(!useCount && (res=Action(acEject))) ClearRemember();
540 bool cFileSource::Status(void)
542 if(needsmount) return Action(acStatus);
546 // -- cFileSources --------------------------------------------------------------
548 bool cFileSources::Load(const char *filename, bool dummy)
550 if(cConfig<cFileSource>::Load(filename,true)) {
557 cFileSource *cFileSources::FindSource(const char *filename)
559 cFileSource *src=First();
561 if(startswith(filename,src->RealBaseDir())) return src;