#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

#include "ppport.h"
#include <sys/types.h>
#include <attr/xattr.h>

static inline int handle2fd(SV *sv)
{
  IO *io;
  return (SvROK(sv) && (io=sv_2io(sv))
	  ? IoDIRP(io) ? my_dirfd(IoDIRP(io)) : PerlIO_fileno(IoIFP(io))
	  : -1);
}

MODULE = Linux::UserXAttr	PACKAGE = Linux::UserXAttr

PROTOTYPES: DISABLE

void
setxattr(handle, name, value, flags=0)
     SV* handle
     char *name
     SV *value
     int flags
  PPCODE:
{
  STRLEN vlen;
  int fd=handle2fd(handle);
  char *vcp;
  vcp=SvPV(value, vlen);

  if( fd>=0 ) {			/* file handle */
    if( !fsetxattr(fd, name, vcp, vlen, flags) ) {
      mPUSHi(1);
    }
  } else {			/* file name */
    STRLEN len;
    char *cp=SvPV(handle, len);
    char path[len+1];
    memcpy(path, cp, len);
    path[len]=0;
    if( !setxattr(path, name, vcp, vlen, flags) ) {
      mPUSHi(1);
    }
  }
}

void
getxattr(handle, name)
     SV* handle
     char *name
  PPCODE:
{
  int fd=handle2fd(handle);
  char buf[4096];
  ssize_t rc;

  errno=0;
  if( fd>=0 ) {
    if( (rc=fgetxattr(fd, name, buf, sizeof(buf)))>=0 ) {
      if(errno==ERANGE && rc>sizeof(buf)) {
	char buf2[rc+1];
	if( (rc=fgetxattr(fd, name, buf2, rc))>=0 ) {
	  mPUSHs(newSVpvn(buf2, rc));
	}
      } else {
	mPUSHs(newSVpvn(buf, rc));
      }
    }
  } else {			/* file name */
    STRLEN len;
    char *cp=SvPV(handle, len);
    char path[len+1];
    memcpy(path, cp, len);
    path[len]=0;
    if( (rc=getxattr(path, name, buf, sizeof(buf)))>=0 ) {
      if(errno==ERANGE && rc>sizeof(buf)) {
	char buf2[rc+1];
	if( (rc=getxattr(path, name, buf2, rc))>=0 ) {
	  mPUSHs(newSVpvn(buf2, rc));
	}
      } else {
	mPUSHs(newSVpvn(buf, rc));
      }
    }
  }
}

void
listxattr(handle)
     SV* handle
  PPCODE:
{
  int fd=handle2fd(handle);
  char buf[4096];
  ssize_t rc;

  errno=0;
  if( fd>=0 ) {
    if( (rc=flistxattr(fd, buf, sizeof(buf)))>=0 ) {
      if(errno==ERANGE && rc>sizeof(buf)) {
	char buf2[rc];
	if( (rc=flistxattr(fd, buf2, rc))>=0 ) {
	  char *cp=buf2;
	  while( cp<buf2+rc ) {
	    int len=strlen(cp);
	    mXPUSHs(newSVpvn(cp, len));
	    cp+=len+1;
	  }
	}
      } else {
	char *cp=buf;
	while( cp<buf+rc ) {
	  int len=strlen(cp);
	  mXPUSHs(newSVpvn(cp, len));
	  cp+=len+1;
	}
      }
    }
  } else {			/* file name */
    STRLEN len;
    char *cp=SvPV(handle, len);
    char path[len+1];
    memcpy(path, cp, len);
    path[len]=0;
    if( (rc=listxattr(path, buf, sizeof(buf)))>=0 ) {
      if(errno==ERANGE && rc>sizeof(buf)) {
	char buf2[rc];
	if( (rc=listxattr(path, buf2, rc))>=0 ) {
	  char *cp=buf2;
	  while( cp<buf2+rc ) {
	    int len=strlen(cp);
	    mXPUSHs(newSVpvn(cp, len));
	    cp+=len+1;
	  }
	}
      } else {
	char *cp=buf;
	while( cp<buf+rc ) {
	  int len=strlen(cp);
	  mXPUSHs(newSVpvn(cp, len));
	  cp+=len+1;
	}
      }
    }
  }
}

void
removexattr(handle, name)
     SV* handle
     char *name
  PPCODE:
{
  int fd=handle2fd(handle);

  if( fd>=0 ) {
    if( !fremovexattr(fd, name) ) {
      mPUSHi(1);
    }
  } else {			/* file name */
    STRLEN len;
    char *cp=SvPV(handle, len);
    char path[len+1];
    memcpy(path, cp, len);
    path[len]=0;
    if( !removexattr(path, name) ) {
      mPUSHi(1);
    }
  }
}

void
lsetxattr(path, name, value, flags=0)
     char *path
     char *name
     SV *value
     int flags
  PPCODE:
{
  STRLEN len;
  char *cp=SvPV(value, len);
  if( !lsetxattr(path, name, cp, len, flags) ) {
    mPUSHi(1);
  }
}

void
lgetxattr(path, name)
     char *path
     char *name
  PPCODE:
{
  char buf[4096];
  ssize_t rc;

  errno=0;
  if( (rc=lgetxattr(path, name, buf, sizeof(buf)))>=0 ) {
    if(errno==ERANGE && rc>sizeof(buf)) {
      char buf2[rc+1];
      if( (rc=lgetxattr(path, name, buf2, rc))>=0 ) {
	mPUSHs(newSVpvn(buf2, rc));
      }
    } else {
      mPUSHs(newSVpvn(buf, rc));
    }
  }
}

void
lremovexattr(path, name)
     char *path
     char *name
  PPCODE:
{
  if( !lremovexattr(path, name) ) {
    mPUSHi(1);
  }
}

void
llistxattr(path)
     char *path
  PPCODE:
{
  char buf[4096];
  ssize_t rc;

  errno=0;
  if( (rc=llistxattr(path, buf, sizeof(buf)))>=0 ) {
    if(errno==ERANGE && rc>sizeof(buf)) {
      char buf2[rc];
      if( (rc=llistxattr(path, buf2, rc))>=0 ) {
	char *cp=buf2;
	while( cp<buf2+rc ) {
	  int len=strlen(cp);
	  mXPUSHs(newSVpvn(cp, len));
	  cp+=len+1;
	}
      }
    } else {
      char *cp=buf;
      while( cp<buf+rc ) {
	int len=strlen(cp);
	mXPUSHs(newSVpvn(cp, len));
	cp+=len+1;
      }
    }
  }
}

BOOT:
{
  HV *stash=gv_stashpv ("Linux::UserXAttr", 0);
  newCONSTSUB(stash, "XATTR_CREATE",  newSViv (XATTR_CREATE));
  newCONSTSUB(stash, "XATTR_REPLACE", newSViv (XATTR_REPLACE));
}

## Local Variables: ##
## mode: c ##
## End: ##