/* ad_mmap provides interfaces to memory mapped files. as this is the
 * case, we don't have to deal w/ temporary buffers such as
 * ad_data. the ad_mmap routines are designed to not interact w/ the
 * ad_read/ad_write routines to avoid confusion.
 */

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>

#include <atalk/adouble.h>

/* i'm currently assuming everybody has mmap. this is lame, but 
 * some of the *bsd's don't define this even though they should.
 */
#ifndef _POSIX_MAPPED_FILES
#define _POSIX_MAPPED_FILES
#endif

#ifdef _POSIX_MAPPED_FILES
#include <sys/mman.h>
#else
/* don't have mmap. it doesn't matter what these are, but they might
 * as well be consistent with the "real" defines */
#define PROT_READ     0x1
#define PROT_WRITE    0x2
#define MAP_READ      0x1
#define MAP_WRITE     0x2
#define MAP_SHARED    0x01
#define MAP_PRIVATE   0x02
#endif

#ifndef __inline__
#define __inline__
#endif


static __inline__ void *ad_mmap(const size_t length, const int prot,
				const int flags, const int fd, 
				const off_t offset)
{
#ifndef _POSIX_MAPPED_FILES
  return (void *) -1;
#else
  return mmap(0, length, prot, flags, fd, offset);
#endif
}

/* this just sets things up for mmap. as mmap can handle offsets,
 * we need to reset the file position before handing it off */
void *ad_mmapread(struct adouble *ad, const u_int32_t eid, 
		  const off_t off, const size_t buflen)
{
    /* data fork */
    if ( eid == ADEID_DFORK ) {
      if ( lseek( ad->ad_df.adf_fd, 0, SEEK_SET ) < 0 ) {
	perror( "df lseek" );
	return (void *) -1;
      }
      ad->ad_df.adf_off = 0;
      return ad_mmap(buflen, PROT_READ | PROT_WRITE, MAP_PRIVATE, 
		     ad->ad_df.adf_fd, off);

    }

    /* resource fork */
    if ( lseek( ad->ad_hf.adf_fd, 0, SEEK_SET ) < 0 ) {
      perror( "hf lseek" );
      return (void *) -1;
    }
    ad->ad_hf.adf_off = 0;
    return ad_mmap(buflen, PROT_READ | PROT_WRITE, MAP_PRIVATE, 
		   ad->ad_hf.adf_fd, ad->ad_eid[eid].ade_off + off);
}


/* to do writeable mmaps correctly, we actually need to make sure that
 * the file to be mapped is large enough. that's what all the initial
 * mess is for. */
void *ad_mmapwrite(struct adouble *ad, const u_int32_t eid,
		   off_t off, const int end, const size_t buflen)
{
    struct stat st;

    /* data fork */
    if ( eid == ADEID_DFORK ) {
        if ( fstat( ad->ad_df.adf_fd, &st ) < 0 ) {
	    return (void *) -1;
        }

	if ( end ) {
	    off = st.st_size - off;
	}

	/* make sure the file is large enough */
	if (st.st_size < buflen + off) 
	  ftruncate(ad->ad_df.adf_fd, buflen + off);

	if ( lseek( ad->ad_df.adf_fd, 0, SEEK_SET ) < 0 ) {
	  return (void *) -1;
	}
	ad->ad_df.adf_off = 0;
	return ad_mmap(buflen, PROT_READ | PROT_WRITE, MAP_SHARED,
		       ad->ad_df.adf_fd, off);
    }

    
    if ( fstat( ad->ad_hf.adf_fd, &st ) < 0 ) {
        return (void *) -1;
    }
    
    if ( end ) {
	off = ad->ad_eid[ eid ].ade_len - off;
    }
    
    off += ad->ad_eid[eid].ade_off;

    /* make sure the file is large enough */
    if (st.st_size < buflen + off) 
      ftruncate(ad->ad_hf.adf_fd, buflen + off);

    if ( lseek( ad->ad_hf.adf_fd, 0, SEEK_SET ) < 0 ) {
      return (void *) -1;
    }
    ad->ad_hf.adf_off = 0;
    return ad_mmap(buflen, PROT_READ | PROT_WRITE, MAP_SHARED,
		   ad->ad_hf.adf_fd, off);
}
