The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

#include "ppport.h"
#include "portable.h"


#define MAX_INITIAL_VALUELEN_VARNAME "File::ExtAttr::MAX_INITIAL_VALUELEN"
                                        /* Richard, fixme! */


MODULE = File::ExtAttr        PACKAGE = File::ExtAttr		

PROTOTYPES: ENABLE


int 
_setfattr (path, attrname, attrvalueSV, flags = 0)
         const char *path
         const char *attrname
         SV * attrvalueSV
         HV * flags
    PREINIT:
        STRLEN slen;
        char * attrvalue;
        int rc;

    CODE:
        attrvalue = SvPV(attrvalueSV, slen);
        rc = portable_setxattr(path, attrname, attrvalue, slen, flags);
        if (rc < 0)
          errno = -rc;
        RETVAL = (rc == 0);

    OUTPUT: 
        RETVAL


int 
_fsetfattr (fd, attrname, attrvalueSV, flags = 0)
         int fd
         const char *attrname
         SV * attrvalueSV
         HV * flags
    PREINIT:
        STRLEN slen;
        char * attrvalue;
        int rc;

    CODE:
        attrvalue = SvPV(attrvalueSV, slen);
        rc = portable_fsetxattr(fd, attrname, attrvalue, slen, flags);
        if (rc < 0)
          errno = -rc;
        RETVAL = (rc == 0);

    OUTPUT: 
        RETVAL


SV *
_getfattr(path, attrname, flags = 0)
        const char *path
        const char *attrname
        HV * flags
   PREINIT:
        char * attrvalue;
        int attrlen;
        ssize_t buflen;

   CODE:
        buflen = portable_lenxattr(path, attrname, flags);
        if (buflen <= 0)
	  buflen = SvIV(get_sv(MAX_INITIAL_VALUELEN_VARNAME, FALSE));

        attrvalue = NULL;
        Newz(1, attrvalue, buflen, char);

        attrlen = portable_getxattr(path, attrname, attrvalue, buflen, flags);
        if (attrlen < 0){

            //key not found, just return undef
            if(errno == ENOATTR){
                Safefree(attrvalue);
                errno = -attrlen;
                XSRETURN_UNDEF;

            //return undef
            }else{
                Safefree(attrvalue);
                errno = -attrlen;
                XSRETURN_UNDEF;
            }
        }

        RETVAL = newSVpv(attrvalue, attrlen);
        Safefree(attrvalue);

    OUTPUT:
        RETVAL


SV *
_fgetfattr(fd, attrname, flags = 0)
        int fd
        const char *attrname
        HV * flags
   PREINIT:
        char * attrvalue;
        int attrlen;
        ssize_t buflen;

   CODE:
        buflen = portable_flenxattr(fd, attrname, flags);
        if (buflen <= 0)
	  buflen = SvIV(get_sv(MAX_INITIAL_VALUELEN_VARNAME, FALSE));

        attrvalue = NULL;
        Newz(1, attrvalue, buflen, char);

        attrlen = portable_fgetxattr(fd, attrname, attrvalue, buflen, flags);
        if (attrlen < 0){

            //key not found, just return undef
            if(errno == ENOATTR){
                Safefree(attrvalue);
                errno = -attrlen;
                XSRETURN_UNDEF;

            //return undef
            }else{
                Safefree(attrvalue);
                errno = -attrlen;
                XSRETURN_UNDEF;
            }
        }

        RETVAL = newSVpv(attrvalue, attrlen);
        Safefree(attrvalue);

    OUTPUT:
        RETVAL


int 
_delfattr (path, attrname, flags = 0)
        const char *path
        const char *attrname
        HV * flags
    PREINIT:
        int rc;

    CODE:
        rc = portable_removexattr(path, attrname, flags);
        if (rc < 0)
          errno = -rc;
        RETVAL = (rc == 0);
    
    OUTPUT: 
        RETVAL


int 
_fdelfattr (fd, attrname, flags = 0)
        int fd
        const char *attrname
        HV * flags
    PREINIT:
        int rc;

    CODE:
        rc = portable_fremovexattr(fd, attrname, flags);
        if (rc < 0)
          errno = -rc;
        RETVAL = (rc == 0);
    
    OUTPUT: 
        RETVAL

void
_listfattr (path, fd, flags = 0)
        const char *path
        int fd
        HV * flags
    PREINIT:
        ssize_t size, ret;
        char *namebuf = NULL;
        char *nameptr;

    PPCODE:
        if(fd == -1)
            size = portable_listxattr(path, NULL, 0, flags);
        else
            size = portable_flistxattr(fd, NULL, 0, flags);

        if (size < 0)
        {
            errno = -(int) size;
            XSRETURN_UNDEF;
        } else if (size == 0)
        {
            XSRETURN_EMPTY;
        }

        namebuf = malloc(size);

        if (fd == -1)
            ret = portable_listxattr(path, namebuf, size, flags);
        else
            ret = portable_flistxattr(fd, namebuf, size, flags);

        // There could be a race condition here, if someone adds a new
        // attribute between the two listxattr calls. However it just means we
        // might return ERANGE.

        if (ret < 0)
        {
            free(namebuf);
            errno = -ret;
            XSRETURN_UNDEF;
        } else if (ret == 0)
        {
            free(namebuf);
            XSRETURN_EMPTY;
        }

        nameptr = namebuf;

        while(nameptr < namebuf + ret)
        {
          char *endptr = nameptr;
          while(*endptr++ != '\0');

          // endptr will now point one past the end..

          XPUSHs(sv_2mortal(newSVpvn(nameptr, endptr - nameptr - 1)));

          // nameptr could now point past the end of namebuf
          nameptr = endptr;
        }

        free(namebuf);

void
_listfattrns (path, fd, flags = 0)
        const char *path
        int fd
        HV * flags
    PREINIT:
        ssize_t size, ret;
        char *namebuf = NULL;
        char *nameptr;

    PPCODE:
        if(fd == -1)
            size = portable_listxattrns(path, NULL, 0, flags);
        else
            size = portable_flistxattrns(fd, NULL, 0, flags);

        if (size < 0)
        {
            errno = -(int) size;
            XSRETURN_UNDEF;
        } else if (size == 0)
        {
            XSRETURN_EMPTY;
        }

        namebuf = malloc(size);

        if (fd == -1)
            ret = portable_listxattrns(path, namebuf, size, flags);
        else
            ret = portable_flistxattrns(fd, namebuf, size, flags);

        // There could be a race condition here, if someone adds a new
        // attribute between the two listxattr calls. However it just means we
        // might return ERANGE.

        if (ret < 0)
        {
            free(namebuf);
            errno = -ret;
            XSRETURN_UNDEF;
        } else if (ret == 0)
        {
            free(namebuf);
            XSRETURN_EMPTY;
        }

        nameptr = namebuf;

        while(nameptr < namebuf + ret)
        {
          char *endptr = nameptr;
          while(*endptr++ != '\0');

          // endptr will now point one past the end..

          XPUSHs(sv_2mortal(newSVpvn(nameptr, endptr - nameptr - 1)));

          // nameptr could now point past the end of namebuf
          nameptr = endptr;
        }

        free(namebuf);