2 * MP3/MPlayer plugin to VDR (C++)
4 * (C) 2001-2006 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 #if APIVERSNUM >= 10315
189 int cFileObj::Compare(const cListObject &ListObject) const
191 cFileObj *obj=(cFileObj *)&ListObject;
192 if(type==otParent) return obj->type==otParent ? 0:-1;
193 if(obj->type==otParent) return 1;
194 if(type==otBase) return obj->type==otBase ? 0:1;
195 if(obj->type==otBase) return -1;
196 if(type!=obj->type) {
197 if(type==otFile) return 1;
200 return strcasecmp(path,obj->path);
203 bool cFileObj::operator<(const cListObject &ListObject)
205 cFileObj *obj=(cFileObj *)&ListObject;
206 if(type==otParent) return obj->type==otParent ? false:true;
207 if(obj->type==otParent) return false;
208 if(type==otBase) return false;
209 if(obj->type==otBase) return true;
210 if(type!=obj->type) {
211 if(type==otFile) return false;
214 return strcasecmp(path,obj->path)<0;
218 void cFileObj::SplitAndSet(const char *Path)
220 free(subdir); subdir=0;
223 int l=strlen(source->BaseDir());
224 if(!strncasecmp(Path,source->BaseDir(),l)) p+=l+1;
226 l=strlen(source->RealBaseDir());
227 if(!strncasecmp(Path,source->RealBaseDir(),l)) p+=l+1;
229 char buff[strlen(Path)+5];
233 char real[PATH_MAX+1];
234 if(!realpath(buff,real)) {
235 if(errno!=ENOENT && errno!=ENOTDIR)
236 esyslog("ERROR: realpath: %s: %s",buff,strerror(errno));
240 if(!strncasecmp(real,source->RealBaseDir(),l))
242 const char *r=index(p,'/');
244 esyslog("ERROR: can't find source basedir in '%s'. Outside source?",Path);
248 strn0cpy(buff,Path,r-Path+1);
255 const char *s=rindex(p,'/');
258 subdir=MALLOC(char,l);
259 if(subdir) strn0cpy(subdir,p,l);
266 void cFileObj::SetName(const char *Name)
269 name=Name ? strdup(Name):0;
273 void cFileObj::Set(void)
276 asprintf(&path,subdir ? "%2$s/%1$s":"%s",name,subdir);
277 free(fpath); fpath=0;
278 MakeFullName(&fpath,name);
281 void cFileObj::MakeFullName(char **fp, const char *Name)
283 asprintf(fp,subdir ? "%1$s/%3$s/%2$s":"%s/%s",source->BaseDir(),Name,subdir);
286 bool cFileObj::GuessType(void)
289 if(!stat64(fpath,&ds)) {
290 if(S_ISREG(ds.st_mode)) type=otFile;
291 else if(S_ISDIR(ds.st_mode)) type=subdir ? otDir:otBase;
298 bool cFileObj::Exists(void)
302 if(!stat64(fpath,&ds) &&
303 S_ISREG(ds.st_mode) &&
304 !access(fpath,R_OK)) return true;
309 bool cFileObj::TestName(const char *newName)
314 MakeFullName(&fname,newName);
315 if(access(fname,F_OK)==0) r=true;
321 bool cFileObj::Rename(const char *newName)
326 MakeFullName(&fname,newName);
327 if(access(fname,F_OK) && (!rename(fpath,fname))) {
336 bool cFileObj::Create(const char *newName)
341 MakeFullName(&fname,newName);
343 if(access(fname,F_OK) && (newf=fopen(fname,"w"))) {
353 bool cFileObj::Delete(void)
355 if(type==otFile && !unlink(fpath)) return true;
359 // -- cDirList --------------------------------------------------------------
361 bool cDirList::Load(cFileSource *src, const char *subdir, const char * const *excl)
363 static const char *excl_s[] = { ".*",0 };
367 if(subdir) Add(new cFileObj(src,subdir,"..",otParent));
369 if(ScanDir(src,subdir,stDir,0,0,false)) {
371 if(!excl) excl=excl_s;
372 if(ScanDir(src,subdir,stFile,src->Include(),excl,false)) res=true;
378 void cDirList::DoItem(cFileSource *src, const char *subdir, const char *name)
380 Add(new cFileObj(src,subdir,name,otype));
383 // -- cFileSource --------------------------------------------------------------
385 cFileSource::cFileSource(void)
387 browsedir=browseparent=0;
388 basedir=realbasedir=description=0; useCount=0;
390 include=0; incCount=0;
393 cFileSource::cFileSource(const char *Basedir, const char *Description, const bool NeedsMount, const char *Include)
395 browsedir=browseparent=0;
396 basedir=realbasedir=description=0; useCount=0;
397 include=0; incCount=0;
398 Set(Basedir,Description,NeedsMount,Include);
401 cFileSource::~cFileSource()
407 void cFileSource::Clear(void)
409 free(basedir); basedir=0;
410 free(realbasedir); realbasedir=0;
411 free(description); description=0;
412 for(int i=0; i<incCount; i++) free(include[i]);
413 free(include); include=0; incCount=0;
416 void cFileSource::Set(const char *Basedir, const char *Description, const bool NeedsMount, const char *Include)
419 basedir=strdup(Basedir);
420 description=strdup(Description);
423 char *s=index(Include,'/');
424 int l=s ? s-Include : strlen(Include);
426 char **s=(char **)realloc(include,(incCount+2)*sizeof(char *));
429 include[incCount]=strndup(Include,l);
434 Include+=l+(s ? 1:0);
439 printf("sources: filesource %s includes (count=%d):",basedir,incCount);
440 for(int i=0; i<incCount; i++) printf(" '%s'",include[i]);
444 printf("sources: filesource %s has no includes set\n",basedir);
446 needsmount=NeedsMount;
448 realbasedir=MALLOC(char,PATH_MAX+1);
449 if(realpath(basedir,realbasedir)) {
450 if(strcmp(basedir,realbasedir)) { esyslog("WARNING: source base %s expands to %s",basedir,realbasedir); }
454 case EACCES: esyslog("ERROR: source base %s permission denied",basedir); break;
455 case ENOENT: esyslog("ERROR: source base %s not found",basedir); break;
456 case ENOTDIR: esyslog("ERROR: source base %s has invalid path",basedir); break;
457 default: esyslog("ERROR: source base %s realpath: %s",basedir,strerror(errno)); break;
459 strn0cpy(realbasedir,basedir,PATH_MAX);
463 void cFileSource::SetRemember(const char *dir, const char *parent)
466 if(dir) browsedir=strdup(dir);
467 if(parent) browseparent=strdup(parent);
470 void cFileSource::ClearRemember(void)
472 free(browsedir); browsedir=0;
473 free(browseparent); browseparent=0;
476 bool cFileSource::GetRemember(char * &dir, char * &parent)
480 if(browseparent) parent=strdup(browseparent);
481 dir=strdup(browsedir);
487 bool cFileSource::Parse(char *s)
489 char base[256], des[256], incl[256];
491 if((n=sscanf(s,"%255[^;];%255[^;];%d;%255[^;]",base,des,&needsmount,incl))>=3) {
492 char *base2=skipspace(stripspace(base));
494 while(l>0 && base2[l-1]=='/') {
495 esyslog("WARNING: removing trailing '/' from base %s",base2);
499 Set(base2,skipspace(stripspace(des)),needsmount!=0,n>3?skipspace(stripspace(incl)):0);
501 // do some checking of the basedir and issue a warning if apropriate
502 if(access(realbasedir,R_OK)) { esyslog("WARNING: source base %s not found/permission denied",realbasedir); }
505 if(lstat64(realbasedir,&ds)) { esyslog("WARNING: can't stat source base %s",realbasedir); }
506 else if(!S_ISDIR(ds.st_mode)) { esyslog("WARNING: source base %s is not a directory",realbasedir); }
513 bool cFileSource::Action(eAction act)
515 static char *str[] = { "mount","unmount","eject","status" };
518 asprintf(&cmd,"%s %s %s",mountscript,str[act],basedir);
519 bool res=(system(cmd)==0);
524 bool cFileSource::Mount(void)
527 if(needsmount && (res=Action(acMount))) ClearRemember();
531 bool cFileSource::Unmount(void)
535 if(!useCount && (res=Action(acUnmount))) ClearRemember();
540 bool cFileSource::Eject(void)
544 if(!useCount && (res=Action(acEject))) ClearRemember();
549 bool cFileSource::Status(void)
551 if(needsmount) return Action(acStatus);
555 // -- cFileSources --------------------------------------------------------------
557 bool cFileSources::Load(const char *filename, bool dummy)
559 if(cConfig<cFileSource>::Load(filename,true)) {
566 cFileSource *cFileSources::FindSource(const char *filename)
568 cFileSource *src=First();
570 if(startswith(filename,src->RealBaseDir())) return src;