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
28 #include <sys/types.h>
33 #include <vdr/tools.h>
39 // ----------------------------------------------------------------
41 const char *mountscript = "mount.sh";
43 char *Quote(const char *str)
45 char *nstr=MALLOC(char,strlen(str)*2);
50 case '\\': // backslash
51 case '\"': // double quote
52 case '`': // back tick
61 char *AddPath(const char *dir, const char *filename)
64 asprintf(&name,"%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 // -- cScanDir --------------------------------------------------------------
89 bool cScanDir::ScanDir(cFileSource *src, const char *subdir, eScanType type, const char * const *spec, const char * const *excl, bool recursiv)
93 asprintf(&dir,subdir ? "%s/%s":"%s",src->BaseDir(),subdir);
97 while((e=readdir64(d))) {
98 if(!strcmp(e->d_name,".") || !strcmp(e->d_name,"..")) continue;
100 if(!(f=AddPath(dir,e->d_name))) continue;
102 if(stat64(f,&st)<0) {
103 esyslog("ERROR: stat(1) %s: %s",f,strerror(errno));
106 if(S_ISLNK(st.st_mode)) {
107 char *of=f; f=ReadLink(of); free(of);
109 if(stat64(f,&st)<0) {
110 esyslog("ERROR: stat(2) %s: %s",f,strerror(errno));
114 if(S_ISDIR(st.st_mode)) {
115 if(type==stFile && recursiv) {
117 asprintf(&s,subdir ? "%2$s/%1$s":"%s",e->d_name,subdir);
118 res=ScanDir(src,s,type,spec,excl,recursiv);
123 if(type!=stDir) continue;
125 if(S_ISREG(st.st_mode)) {
126 if(type!=stFile) continue;
129 for(const char * const *m=spec; *m; m++) {
130 int n=fnmatch(*m,e->d_name,FNM_CASEFOLD);
131 if(n==0) { ok=true; break; }
132 if(n!=FNM_NOMATCH) esyslog("ERROR: fnmatch(1) %s: %s",*m,strerror(errno));
138 for(const char * const *m=excl; *m; m++) {
139 int n=fnmatch(*m,e->d_name,FNM_CASEFOLD);
140 if(n==0) { ok=false; break; }
141 if(n!=FNM_NOMATCH) esyslog("ERROR: fnmatch(2) %s: %s",*m,strerror(errno));
146 DoItem(src,subdir,e->d_name);
151 esyslog("ERROR: opendir %s: %s",dir,strerror(errno));
158 // -- cFileObj --------------------------------------------------------------
160 cFileObj::cFileObj(cFileSource *Source, const char *Subdir, const char *Name, const eObjType Type)
164 subdir=Subdir ? strdup(Subdir):0;
165 name=Name ? strdup(Name):0;
170 cFileObj::cFileObj(const cFileObj *obj)
174 subdir=obj->subdir ? strdup(obj->subdir):0;
175 name=obj->name ? strdup(obj->name):0;
180 cFileObj::~cFileObj()
188 int cFileObj::Compare(const cListObject &ListObject) const
190 cFileObj *obj=(cFileObj *)&ListObject;
191 if(type==otParent) return obj->type==otParent ? 0:-1;
192 if(obj->type==otParent) return 1;
193 if(type==otBase) return obj->type==otBase ? 0:1;
194 if(obj->type==otBase) return -1;
195 if(type!=obj->type) {
196 if(type==otFile) return 1;
199 return strcasecmp(path,obj->path);
202 void cFileObj::SplitAndSet(const char *Path)
204 free(subdir); subdir=0;
207 int l=strlen(source->BaseDir());
208 if(!strncasecmp(Path,source->BaseDir(),l)) p+=l+1;
210 l=strlen(source->RealBaseDir());
211 if(!strncasecmp(Path,source->RealBaseDir(),l)) p+=l+1;
213 char buff[strlen(Path)+5];
217 char real[PATH_MAX+1];
218 if(!realpath(buff,real)) {
219 if(errno!=ENOENT && errno!=ENOTDIR)
220 esyslog("ERROR: realpath: %s: %s",buff,strerror(errno));
224 if(!strncasecmp(real,source->RealBaseDir(),l))
226 const char *r=index(p,'/');
228 esyslog("ERROR: can't find source basedir in '%s'. Outside source?",Path);
232 strn0cpy(buff,Path,r-Path+1);
239 const char *s=rindex(p,'/');
242 subdir=MALLOC(char,l);
243 if(subdir) strn0cpy(subdir,p,l);
250 void cFileObj::SetName(const char *Name)
253 name=Name ? strdup(Name):0;
257 void cFileObj::Set(void)
260 asprintf(&path,subdir ? "%2$s/%1$s":"%s",name,subdir);
261 free(fpath); fpath=0;
262 MakeFullName(&fpath,name);
265 void cFileObj::MakeFullName(char **fp, const char *Name)
267 asprintf(fp,subdir ? "%1$s/%3$s/%2$s":"%s/%s",source->BaseDir(),Name,subdir);
270 bool cFileObj::GuessType(void)
273 if(!stat64(fpath,&ds)) {
274 if(S_ISREG(ds.st_mode)) type=otFile;
275 else if(S_ISDIR(ds.st_mode)) type=subdir ? otDir:otBase;
282 bool cFileObj::Exists(void)
286 if(!stat64(fpath,&ds) &&
287 S_ISREG(ds.st_mode) &&
288 !access(fpath,R_OK)) return true;
293 bool cFileObj::TestName(const char *newName)
298 MakeFullName(&fname,newName);
299 if(access(fname,F_OK)==0) r=true;
305 bool cFileObj::Rename(const char *newName)
310 MakeFullName(&fname,newName);
311 if(access(fname,F_OK) && (!rename(fpath,fname))) {
320 bool cFileObj::Create(const char *newName)
325 MakeFullName(&fname,newName);
327 if(access(fname,F_OK) && (newf=fopen(fname,"w"))) {
337 bool cFileObj::Delete(void)
339 if(type==otFile && !unlink(fpath)) return true;
343 // -- cDirList --------------------------------------------------------------
345 bool cDirList::Load(cFileSource *src, const char *subdir, const char * const *excl)
347 static const char *excl_s[] = { ".*",0 };
351 if(subdir) Add(new cFileObj(src,subdir,"..",otParent));
353 if(ScanDir(src,subdir,stDir,0,0,false)) {
355 if(!excl) excl=excl_s;
356 if(ScanDir(src,subdir,stFile,src->Include(),excl,false)) res=true;
362 void cDirList::DoItem(cFileSource *src, const char *subdir, const char *name)
364 Add(new cFileObj(src,subdir,name,otype));
367 // -- cFileSource --------------------------------------------------------------
369 cFileSource::cFileSource(void)
371 browsedir=browseparent=0;
372 basedir=realbasedir=description=0; useCount=0;
374 include=0; incCount=0;
377 cFileSource::cFileSource(const char *Basedir, const char *Description, const bool NeedsMount, const char *Include)
379 browsedir=browseparent=0;
380 basedir=realbasedir=description=0; useCount=0;
381 include=0; incCount=0;
382 Set(Basedir,Description,NeedsMount,Include);
385 cFileSource::~cFileSource()
391 void cFileSource::Clear(void)
393 free(basedir); basedir=0;
394 free(realbasedir); realbasedir=0;
395 free(description); description=0;
396 for(int i=0; i<incCount; i++) free(include[i]);
397 free(include); include=0; incCount=0;
400 void cFileSource::Set(const char *Basedir, const char *Description, const bool NeedsMount, const char *Include)
403 basedir=strdup(Basedir);
404 description=strdup(Description);
407 char *s=index(Include,'/');
408 int l=s ? s-Include : strlen(Include);
410 char **s=(char **)realloc(include,(incCount+2)*sizeof(char *));
413 include[incCount]=strndup(Include,l);
418 Include+=l+(s ? 1:0);
423 printf("sources: filesource %s includes (count=%d):",basedir,incCount);
424 for(int i=0; i<incCount; i++) printf(" '%s'",include[i]);
428 printf("sources: filesource %s has no includes set\n",basedir);
430 needsmount=NeedsMount;
432 realbasedir=MALLOC(char,PATH_MAX+1);
433 if(realpath(basedir,realbasedir)) {
434 if(strcmp(basedir,realbasedir)) { esyslog("WARNING: source base %s expands to %s",basedir,realbasedir); }
438 case EACCES: esyslog("ERROR: source base %s permission denied",basedir); break;
439 case ENOENT: esyslog("ERROR: source base %s not found",basedir); break;
440 case ENOTDIR: esyslog("ERROR: source base %s has invalid path",basedir); break;
441 default: esyslog("ERROR: source base %s realpath: %s",basedir,strerror(errno)); break;
443 strn0cpy(realbasedir,basedir,PATH_MAX);
447 void cFileSource::SetRemember(const char *dir, const char *parent)
450 if(dir) browsedir=strdup(dir);
451 if(parent) browseparent=strdup(parent);
454 void cFileSource::ClearRemember(void)
456 free(browsedir); browsedir=0;
457 free(browseparent); browseparent=0;
460 bool cFileSource::GetRemember(char * &dir, char * &parent)
464 if(browseparent) parent=strdup(browseparent);
465 dir=strdup(browsedir);
471 bool cFileSource::Parse(char *s)
473 char base[256], des[256], incl[256];
475 if((n=sscanf(s,"%255[^;];%255[^;];%d;%255[^;]",base,des,&needsmount,incl))>=3) {
476 char *base2=skipspace(stripspace(base));
478 while(l>0 && base2[l-1]=='/') {
479 esyslog("WARNING: removing trailing '/' from base %s",base2);
483 Set(base2,skipspace(stripspace(des)),needsmount!=0,n>3?skipspace(stripspace(incl)):0);
485 // do some checking of the basedir and issue a warning if apropriate
486 if(access(realbasedir,R_OK)) { esyslog("WARNING: source base %s not found/permission denied",realbasedir); }
489 if(lstat64(realbasedir,&ds)) { esyslog("WARNING: can't stat source base %s",realbasedir); }
490 else if(!S_ISDIR(ds.st_mode)) { esyslog("WARNING: source base %s is not a directory",realbasedir); }
497 bool cFileSource::Action(eAction act)
499 static const char *str[] = { "mount","unmount","eject","status" };
502 asprintf(&cmd,"%s %s %s",mountscript,str[act],basedir);
503 bool res=(system(cmd)==0);
508 bool cFileSource::Mount(void)
511 if(needsmount && (res=Action(acMount))) ClearRemember();
515 bool cFileSource::Unmount(void)
519 if(!useCount && (res=Action(acUnmount))) ClearRemember();
524 bool cFileSource::Eject(void)
528 if(!useCount && (res=Action(acEject))) ClearRemember();
533 bool cFileSource::Status(void)
535 if(needsmount) return Action(acStatus);
539 // -- cFileSources --------------------------------------------------------------
541 bool cFileSources::Load(const char *filename, bool dummy)
543 if(cConfig<cFileSource>::Load(filename,true)) {
550 cFileSource *cFileSources::FindSource(const char *filename)
552 cFileSource *src=First();
554 if(startswith(filename,src->RealBaseDir())) return src;