#include <sys/types.h>

#include <stdlib.h>
#include <unistd.h>

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

#include "ppport.h"

MODULE = File::ReplaceBytes             PACKAGE = File::ReplaceBytes            

ssize_t
pread(PerlIO *fh, SV *buf, ...)
  PROTOTYPE: $$$;$
  PREINIT:
    off_t offset = 0;
    STRLEN len   = 0;

  CODE:
    if( items > 2 ) {
        if (!SvIOK(ST(2)) || SvIV(ST(2)) < 0) {
            errno = EINVAL;
            XSRETURN_IV(-1);
        }
        len = SvIV(ST(2));
    }
/* emulate pread not complaining if nothing to read */
    if (len == 0)
        XSRETURN_IV(0);
    if( items > 3 ) {
        if (!SvIOK(ST(3)) || SvIV(ST(3)) < 0) {
            errno = EINVAL;
            XSRETURN_IV(-1);
        }
        offset = SvIV(ST(3));
    }

    if(!SvOK(buf)) 
        sv_setpvs(buf, "");

    RETVAL = pread(PerlIO_fileno(fh), SvGROW(buf, len), len, offset);
    if (RETVAL > 0) {
        SvCUR_set(buf, RETVAL);
        SvTAINTED_on(buf);
    } else {
        SvCUR_set(buf, 0);
    }

  OUTPUT:
    buf
    RETVAL

ssize_t
pwrite(PerlIO *fh, SV *buf, ...)
  PROTOTYPE: $$;$$
  PREINIT:
    char *bp;
    off_t offset = 0;
    STRLEN len = 0;
    STRLEN userlen = 0;

  CODE:
/* pwrite(2) does not complain if nothing to write, so emulate that */
    if(!SvOK(buf) || SvCUR(buf) == 0)
        XSRETURN_IV(0);
/* length, offset are optional, but offset demands that length also be
 * set by the caller */
    if( items > 2 ) {
        if (!SvIOK(ST(2)) || SvIV(ST(2)) < 0) {
            errno = EINVAL;
            XSRETURN_IV(-1);
        }
        userlen = SvIV(ST(2));
    }
    if( items > 3 ) {
        if (!SvIOK(ST(3)) || SvIV(ST(3)) < 0) {
            errno = EINVAL;
            XSRETURN_IV(-1);
        }
        offset = SvIV(ST(3));
    }

    bp = SvPV(buf, len);
    if (userlen == 0 || userlen > len)
        userlen = len;
    RETVAL = pwrite(PerlIO_fileno(fh), bp, userlen, offset);

  OUTPUT:
    RETVAL

ssize_t
replacebytes(SV *filename, SV *buf, ...)
  PROTOTYPE: $$;$
  PREINIT:
    char *bp;
    int fd, i;
    off_t offset = 0;
    STRLEN len;

  CODE:
    if(!SvOK(buf) || SvCUR(buf) == 0)
        XSRETURN_IV(0);
    if( items > 2 ) {
        if (!SvIOK(ST(2)) || SvIV(ST(2)) < 0) {
            errno = EINVAL;
            XSRETURN_IV(-1);
        }
        offset = SvIV(ST(2));
    }

    /* as otherwise a filename of "bar\0foo" results in a "bar" file which
     * is not the same as what was input */
    bp = SvPV(filename, len);
    for (i = 0; i < len; i++) {
        if (bp[i] == '\0') {
            errno = EINVAL;
            XSRETURN_IV(-1);
        }
    }

    if((fd = open(SvPV_nolen(filename), O_CREAT|O_WRONLY, 0666)) == -1)
        XSRETURN_IV(-1);

    bp = SvPV(buf, len);
    RETVAL = pwrite(fd, bp, len, offset);

    close(fd);

  OUTPUT:
    RETVAL