// It's only a prototype :P
// Use:
//  fuse, uniq, sort, sed, dpkg
//
// To compile:
//  g++ -Wall -pedantic dpkgfs.cpp -o dpkgfs `pkg-config fuse --libs --cflags`
//
// smigacz:/home/urug/code/dpkgfs# ./dpkgfs /mnt/
// smigacz:/home/urug/code/dpkgfs# ls /mnt/ | head -n5
// abuse
// abuse-frabs
// abuse-sfx
// acpid
// acpi-support-base
// smigacz:/home/urug/code/dpkgfs# ls /mnt/abuse
// usr
// smigacz:/home/urug/code/dpkgfs# ls /mnt/abuse/usr/
// games  share
// smigacz:/home/urug/code/dpkgfs# ls /mnt/abuse/usr/games/
// abuse  abuse.sdl
// smigacz:/home/urug/code/dpkgfs# 

#include <string>
#include <cstring>
#include <cstdio>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

#define FUSE_USE_VERSION 26
#include <fuse.h>

using namespace std;

static struct fuse_operations f_oper;

void SplitPathPkgName( const string &path, string &pkgName, string &newPath ) {
    string::size_type pos = path.find_first_of("/", 1);
    if ( pos == string::npos )
        pos = path.size()-1;
    else
        pos--; // Pomin slash na koncu

    pkgName = path.substr(1, pos);
    newPath = path.substr(pos+1);
}

FILE *PipeOpen( const char *cmd ) {
    FILE *fp = popen(cmd, "r");
    if ( !fp  )
        return 0;

    int sign = getc(fp);
    if ( sign == EOF )
        return 0;
    ungetc(sign, fp);

    return fp;
}

void PipeClose( FILE *p ) {
    pclose(p);
}

FILE *DPKG_PkgList( void *buf, fuse_fill_dir_t filler ) {
    return PipeOpen("dpkg --get-selections | sed 's/^\\([^[:space:]]*\\).*/\\1/'");
}

FILE *DPKG_FileList( const string &path, void *buf, fuse_fill_dir_t filler ) {
    string pkgName, newPath;
    SplitPathPkgName(path, pkgName, newPath);

    // Tutaj moga sie dziac zle rzeczy
    string cmd("dpkg -L ");
    cmd += pkgName + "| sed -n 's|^\\(" + newPath + "/\\)\\([^/]*\\).*|\\2|p' | sort | uniq";

    return PipeOpen(cmd.c_str());
}

int d_getattr(const char *path, struct stat *stbuf) {
    memset(stbuf, 0, sizeof(struct stat));
    string p = path;
    if ( p == "/" )
        stbuf->st_mode = S_IFDIR | 0555;
    else {
        string pkgName, newPath;
        SplitPathPkgName(p, pkgName, newPath);

        if ( newPath.size() == 0 )
            stbuf->st_mode = S_IFDIR | 0555;
        else
            stat(newPath.c_str(), stbuf);
    }

    return 0;
}

int d_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi ) {
    FILE *fp = (string(path) == "/" ? DPKG_PkgList(buf, filler) : DPKG_FileList(path, buf, filler));
    if ( !fp )
        return -1;

    filler(buf, ".", 0, 0);
    filler(buf, "..", 0, 0);

    char plik[200];
    while ( fgets(plik, sizeof(plik)/sizeof(*plik), fp) ) {
        plik[strlen(plik)-1] = '\0';
        filler(buf, plik, 0, 0);
    }

    PipeClose(fp);
    return 0;
}

int d_read(const char *path, char *buf, size_t size, off_t off, struct fuse_file_info *f) {
    string pkgName, newPath;
    SplitPathPkgName(path, pkgName, newPath);

    int fd = open(newPath.c_str(), O_RDONLY);
    lseek(fd, off, SEEK_SET);

    int ret = read(fd, buf, size);
    close(fd);

    return ret;
}

int main( int argc, char **argv ) {
    f_oper.getattr = d_getattr;
    f_oper.readdir = d_readdir;
    f_oper.read = d_read;

    return fuse_main(argc, argv, &f_oper, NULL);
}

