#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]));
}
Vorgefundene Kodierung: UTF-8 | 0
|