Source file: /~heha/hs/cdcat1plugin.zip/src-linux/cdcat1.cpp

#include "fsplugin.h"
#include "xml.h"
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <cstdarg>   // va_list
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>

static char inifilename[260];
volatile bool wantStop;

void debug(const char*t,...) {
#ifdef _DEBUG
 printf("\e[1m");
 va_list va;
 va_start(va,t);
 vprintf(t,va);
 va_end(va);
 printf("\e[0m\n");
#endif
}

static int PluginNumber;
static tProgressProc ProgressProc;
static tLogProc LogProc;
static tRequestProc RequestProc;

EXPORT(int) FsInit(int n,tProgressProc p1,tLogProc p2,tRequestProc p3) {
 debug("%s(%d,%p,%p,%p)",__FUNCTION__,n,p1,p2,p3);
 PluginNumber=n;
 ProgressProc=p1;
 LogProc=p2;
 RequestProc=p3;
 return 0;
}

struct FindHandle{
 int iItem;
 const Xml::Node*item;
 operator HANDLE() {return HANDLE(this);}	    // Typecast-Operator
 FindHandle(const Xml::Node*n):item(n),iItem(0) {}
};

struct LoadedFile{
 Xml*xml;	// !=0 when loaded
 int dirty;	// counts number of changes
 DWORD packer_id;
 FILETIME getLastMod() const;
 char displayname[64];
 char filename[260];
 bool load();
 bool save();
 ~LoadedFile();
 static bool convertFile(int,int,const char*);
 static bool unpackFile(int&,const char*);
 static bool packFile(int&,const char*);
private:
 int open(int=O_RDONLY) const;
};

LoadedFile::~LoadedFile() {
 if (xml) {
  if (dirty) save();
  delete xml;
 }
}

int LoadedFile::open(int creation) const{
 debug("%s(\"%s\",%d)",__FUNCTION__,filename,creation);
 return ::open(filename,creation);
}

FILETIME LoadedFile::getLastMod() const{
 union{
  long long ll;
  FILETIME ft;
 }ret={-1};
 struct stat statbuf;
 stat(filename,&statbuf);
 ret.ll = statbuf.st_mtim.tv_nsec/100;
 return ret.ft;
}

bool LoadedFile::convertFile(int hr,int hw,const char*cmd) {
 auto pid=fork();
 if (!pid) {
  dup2(hr,0); close(hr);
  dup2(hw,1); close(hw);
  char*argv[]={const_cast<char*>(cmd),0};
  if (execvp(argv[0],argv)<0) exit(0);
 }
 close(hr);
 close(hw);
 return true;
}


// Packt Datei im Worker-Thread aus und tauscht das Dateihandle gegen das Read-Pipe-Handle
bool LoadedFile::unpackFile(int&h,const char*cmd) {
 int pipefd[2];
 if (pipe2(pipefd,0)<0) return false;
 if (!convertFile(h,pipefd[1],cmd)) return false;
 h=pipefd[0];
 return true;
}

// Packt Datei im Worker-Thread ein und tauscht das Dateihandle gegen das Write-Pipe-Handle
bool LoadedFile::packFile(int&h,const char*cmd) {
 int pipefd[2];
 if (pipe2(pipefd,0)<0) return false;
 if (!convertFile(pipefd[0],h,cmd)) return false;
 h=pipefd[1];
 return true;
}

bool LoadedFile::load() {
 if (xml) return true;	// bereits geladen
 auto h=open();
 if (h<0) return false;
 debug("%s(\"%s\") Start",__FUNCTION__,filename);
 auto br=read(h,&packer_id,4);
 if (br!=4) return false;
 lseek(h,0,SEEK_SET);
// Kann eine Weile dauern! ProgressProc() aufrufen
 ProgressProc(PluginNumber,filename,"RAM-Speicher",0);
 bool ret=true;
// unpackFile() geht schnell (blockiert kaum),
// denn das ersetzt <h> durch eine Pipe mit einem Fütter-Thread
 switch (packer_id) {
  case 0x00088B1F: ret=unpackFile(h,"gunzip"); break;
  case 0x31685A42: ret=unpackFile(h,"bunzip2"); break;
 }
 if (ret) {
  xml=new Xml;
  xml->hStream=h;
  ret=xml->parse();
 }
 ProgressProc(PluginNumber,filename,"RAM-Speicher",100);
 debug("%s(\"%s\")->%s",__FUNCTION__,filename,ret?"success":"fail");
 if (!ret) {
  debug("Error at %s:%u:%u",filename,xml->line+1,xml->pos+1);
  delete xml; xml=0;
 }
 return ret;
}

bool LoadedFile::save() {
// make backup!!
 auto h=open(O_CREAT);
 if (h<0) return false;
 switch (packer_id) {
  case 0x00088B1F: if (!packFile(h,"gzip")) return false; break;
  case 0x31685A42: if (!packFile(h,"bzip2")) return false; break;
 }
 xml->hStream=h;
 return xml->serialize();
}

class LoadedFiles {
 int fill;
 LoadedFile a[10];
public:
 const LoadedFile&operator[](int i) const {return a[i];}
 LoadedFile&operator[](int i) {return a[i];}
 int size() const {return fill;}
 void size(int s) {fill=s;}
};

static LoadedFiles loadedFiles;

// ähnlich memcmp, aber drittes Argument gibt Vergleichslänge vor
bool memstrcmp(const char*s, const char*e, const char*v) {
 auto l=strlen(v);
 if (e-s!=l) return false;  // Länge muss gleich sein
 return !memcmp(s,v,l);	    // Bytes müssen gleich sein
}

// wie strchr() aber liefert ggf. Zeiger auf terminierende '\0'
static const char*strendchr(const char*s, char c) {
 const char*p=strchr(s,c);
 return p?p:s+strlen(s);
}

static const Xml::Node*StringToNode(const char*s) {
 if (s[0]!='/') return 0;	// Nur absoluter Pfad erlaubt!
 ++s;
 if (!*s) return 0;		// Wurzel = Liste der Dateinamen
 for (int i=0; i<loadedFiles.size(); i++) {
  debug("hier: %d,%s",i,loadedFiles[i].displayname);
  const char*q=strendchr(s,'/');
  if (memstrcmp(s,q,loadedFiles[i].displayname)) {	// Dateiname gefunden
   debug("Dateiname gefunden: %s",loadedFiles[i].filename);
   if (!loadedFiles[i].load()) {
    debug("Kann nicht laden");
    return 0;
   }
   const Xml::Node*n=loadedFiles[i].xml->root;
   debug("Wurzel=%p",n);
   for (const char*q=s; *(q=strendchr(q,'/')); *q?++q:0) {
    auto f=[](const Xml::Node*nn,void*v)->bool {
     auto a=nn->findChildNode(Xml::Node::Attr,"name");
     if (!a) return false;
     return !strcmp(a->value,(const char*)v);
    };
    n=n->findChildNode(Xml::Node::Element,f,(void*)q);
    if (!n) return 0;
   }
   return n;
  }
 }
 return 0;
}

enum{
 eRoot=32,	// erlaubt das Auflisten von Katalogen (falls mehrere; unzulässiges XML)
 eCatalog=16,
 eMedia=8,
 eDir=4,
 eFile=2,
 eOther=1,
};

static unsigned char classify(const Xml::Node*n,unsigned char check=0xFF) {	// Liefert 1-aus-n-Bitmaske
 if (!n) return 0;			// nichts
 if (check&eRoot	&& n->type==Xml::Node::root)		return eRoot;
 if (n->type!=Xml::Node::Element) return 0;	// nichts
 if (check&eCatalog	&& !strcmp(n->name,"catalog"))	return eCatalog;
 if (check&eMedia	&& !strcmp(n->name,"media"))	return eMedia;
 if (check&eDir		&& !strcmp(n->name,"directory"))return eDir;
 if (check&eFile	&& !strcmp(n->name,"file"))	return eFile;
 return check&eOther;
}

static bool setFindData(const Xml::Node*c,WIN32_FIND_DATA&W) {
 if (!c) return false;
 W.dwFileAttributes=classify(c,eFile)?0:FILE_ATTRIBUTE_DIRECTORY;
 auto a=c->findChildNode(Xml::Node::Attr,"time");
 if (a) {
#if 0
  SYSTEMTIME st;
  st.wDayOfWeek=st.wMilliseconds=0;
  if (sscanf(a->value,"%hu-%hu-%hu %hu:%hu:%hu",
	&st.wYear,&st.wMonth,&st.wDay,
	&st.wHour,&st.wMinute,&st.wSecond)==6) {
   FILETIME ft;
   SystemTimeToFileTime(&st,&ft);
   LocalFileTimeToFileTime(&ft,&W.ftLastWriteTime);
  }
#endif
 }
 if (!W.dwFileAttributes) {	// Datei, kein Verzeichnis
  a=c->findChildNode(Xml::Node::Attr,"size");
  if (a) {
   char*s=strdup(a->value);
   char*k=strchr(s,',');
   if (k) *k='.';
   float fsz;
   char kmg[2];
   if (sscanf(s,"%f %1[KMGT]b",
	&fsz,kmg)==2) {
    switch (kmg[0]) {
     case 'T': fsz*=1000;
     case 'G': fsz*=1000;
     case 'M': fsz*=1000;	// Auch wenn das wahrscheinlich nicht stimmt, sieht im Commander besser aus
    }
    unsigned long long sz=fsz*1000;
    W.nFileSizeHigh=DWORD(sz>>32);
    W.nFileSizeLow=DWORD(sz);
   }
   free(s);
  }
 }
 a=c->findChildNode(Xml::Node::Attr,"name");
 if (a) snprintf(W.cFileName,sizeof W.cFileName,"%s",a->value);
 else *W.cFileName=0;		// ohne Name (Datenbank-Bug)
 *W.cAlternateFileName=0;
 return true;
}

static void initfinddata(WIN32_FIND_DATA&W) {
 memset(&W,0,sizeof W);
 W.ftLastWriteTime.dwHighDateTime=DWORD(-1);
 W.ftLastWriteTime.dwLowDateTime=DWORD(-2);
}

EXPORT(HANDLE) FsFindFirst(const char*Path,WIN32_FIND_DATA&W) {
 debug("%s(\e[36m\"%s\"\e[1m,%p)",__FUNCTION__,Path,&W);
 initfinddata(W);
 if (Path[0]=='/' && !Path[1]) {
  W.dwFileAttributes=FILE_ATTRIBUTE_READONLY;		// Kein Verzeichnis zum Öffnen
  FindHandle*lf=new FindHandle(0);
  snprintf(W.cFileName,sizeof W.cFileName,"%s","Neuer Katalog");
  return *lf;
 }
 const Xml::Node*n=StringToNode(Path);
 if (!classify(n,eRoot|eCatalog|eMedia|eDir)) return INVALID_HANDLE_VALUE;
 auto f=[](const Xml::Node*n,void*) {return!!classify(n,eCatalog|eMedia|eDir|eFile);};
 const Xml::Node*c=n->findChildNode(Xml::Node::Element,f,0);
 if (!setFindData(c,W)) return INVALID_HANDLE_VALUE;
 return *(new FindHandle(c));
}

EXPORT(BOOL) FsFindNext(HANDLE Hdl,WIN32_FIND_DATA&W) {
 debug("%s(%p,%p)",__FUNCTION__,Hdl,&W);
 initfinddata(W);
 FindHandle*fh=(FindHandle*)Hdl;
 if (!fh->item) {   // generate loaded files list
  if (++fh->iItem>loadedFiles.size()) return false;
  W.dwFileAttributes=FILE_ATTRIBUTE_DIRECTORY;
  LoadedFile&lf=loadedFiles[fh->iItem-1];
  W.ftLastWriteTime=lf.getLastMod();
  snprintf(W.cFileName,sizeof W.cFileName,"%s",lf.displayname);
  return true;
 }
 ++fh->iItem;
 debug("a:%p",fh->item);
 fh->item=fh->item->next;
 debug("b:%p",fh->item);
 fh->item=fh->item->findElement();
 debug("c:%p",fh->item);
 return setFindData(fh->item,W);
}

EXPORT(int) FsFindClose(HANDLE Hdl) {
 debug("%s(%p)",__FUNCTION__,Hdl);
 delete (FindHandle*)Hdl;
 return 0;
}

EXPORT(void) FsGetDefRootName(char*DefRootName,int maxlen) {
 debug("%s(%s,%d)",__FUNCTION__,DefRootName,maxlen);
 snprintf(DefRootName,maxlen,"%s","CD-Katalog");
}

EXPORT(int) FsExtractCustomIcon(const char*Path,int flags,int&ico) {
 debug("%s(%s,%d)",__FUNCTION__,Path,flags);
 auto n=StringToNode(Path);
 auto c=classify(n,eRoot|eCatalog|eMedia);
 if (c&(eRoot|eCatalog)) {
  ico=open("cdcat1.ico",O_RDONLY);
  return FS_ICON_EXTRACTED;
 }
 if (c&eMedia) {
  ico=open("icon2.ico",O_RDONLY);
  return FS_ICON_EXTRACTED;
 }
 return FS_ICON_USEDEFAULT;
}

static char*trim(char*s) {
 char*d=s;
 while (*d && (unsigned char)*d<=' ') ++d;   // links trimmen: Zeiger verschieben
 char*e=d+strlen(d);
 while (e!=s && (unsigned char)e[-1]<=' ') --e;
 *e=0; 	     	// rechts trimmen: String abhacken
 return d;
}

EXPORT(void) FsSetDefaultParams(FsDefaultParamStruct&dps) {
 debug("%s(%p)",__FUNCTION__,&dps);
 snprintf(inifilename,sizeof inifilename,"%s",dps.DefaultIniName);
 debug("inifilename=%s",inifilename);
 auto f=fopen(inifilename,"r");
 char line[300];
 bool insection=false;
 LoadedFile*lf=&loadedFiles[0];
 while (fgets(line,sizeof line,f)) {
  char*d=trim(line);
  int l=strlen(d);
  if (*d=='[' && d[l-1]==']') {
   debug("Ini-Abschnitt=%s",d);
   insection = memstrcmp(d+1,d+l-1,"cdcat1");
  }else if (insection) {
   char*q=strchr(d,'=');
   if (!q) continue;
   int l1=q-d;
   if (l1>sizeof lf->displayname-1) l1=sizeof lf->displayname-1;
   memcpy(lf->displayname,d,l1);
   lf->displayname[l1]=0;
   l1 = l-l1;	// Länge Dateiname inklusive '\0'
   if (l1>sizeof lf->filename) l1=sizeof lf->filename;
   memcpy(lf->filename,q+1,l1);
   debug("Katalog=%s, Dateiname=%s",lf->displayname,lf->filename);
   lf++;
  }
 }
 loadedFiles.size(int(lf-&loadedFiles[0]));
}
Detected encoding: UTF-80