/* $Header: /usr/people/sam/tiff/libtiff/RCS/tif_vms.c,v 1.21 1996/03/29 16:37:53 sam Rel $ */

/*
 * Copyright (c) 1988-1996 Sam Leffler
 * Copyright (c) 1991-1996 Silicon Graphics, Inc.
 *
 * Permission to use, copy, modify, distribute, and sell this software and 
 * its documentation for any purpose is hereby granted without fee, provided
 * that (i) the above copyright notices and this permission notice appear in
 * all copies of the software and related documentation, and (ii) the names of
 * Sam Leffler and Silicon Graphics may not be used in any advertising or
 * publicity relating to the software without the specific, prior written
 * permission of Sam Leffler and Silicon Graphics.
 * 
 * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
 * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
 * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
 * 
 * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
 * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
 * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
 * OF THIS SOFTWARE.
 */

/*
 * TIFF Library VMS-specific Routines.
 */

#include <stdlib.h>
#include <unixio.h>
#include "tiffiop.h"
#if !HAVE_IEEEFP
#include <math.h>
#endif

#ifdef VAXC
#define NOSHARE noshare
#else
#define NOSHARE
#endif

#ifdef __alpha
/* Dummy entry point for backwards compatibility */
void TIFFModeCCITTFax3(void){}
#endif

static tsize_t
_tiffReadProc(thandle_t fd, tdata_t buf, tsize_t size)
{
    return (read((int) fd, buf, size));
}

static tsize_t
_tiffWriteProc(thandle_t fd, tdata_t buf, tsize_t size)
{
    return (write((int) fd, buf, size));
}

static toff_t
_tiffSeekProc(thandle_t fd, toff_t off, int whence)
{
    return ((toff_t) lseek((int) fd, (off_t) off, whence));
}

static int
_tiffCloseProc(thandle_t fd)
{
    return (close((int) fd));
}

#include <sys/stat.h>

static toff_t
_tiffSizeProc(thandle_t fd)
{
    struct stat sb;
    return (toff_t) (fstat((int) fd, &sb) < 0 ? 0 : sb.st_size);
}

#ifdef HAVE_MMAP
#include <starlet.h>
#include <fab.h>
#include <secdef.h>

/*
 * Table for storing information on current open sections. 
 * (Should really be a linked list)
 */
#define MAX_MAPPED 100
static int no_mapped = 0;
static struct {
    char *base;
    char *top;
    unsigned short channel;
} map_table[MAX_MAPPED];

/* 
 * This routine maps a file into a private section. Note that this 
 * method of accessing a file is by far the fastest under VMS.
 * The routine may fail (i.e. return 0) for several reasons, for
 * example:
 * - There is no more room for storing the info on sections.
 * - The process is out of open file quota, channels, ...
 * - fd does not describe an opened file.
 * - The file is already opened for write access by this process
 *   or another process
 * - There is no free "hole" in virtual memory that fits the
 *   size of the file
 */
static int
_tiffMapProc(thandle_t fd, tdata_t* pbase, toff_t* psize)
{
    char name[256];
    struct FAB fab;
    unsigned short channel;
    char *inadr[2], *retadr[2];
    unsigned long status;
    long size;
    
    if (no_mapped >= MAX_MAPPED)
        return(0);
    /*
     * We cannot use a file descriptor, we
     * must open the file once more.
     */
    if (getname((int)fd, name, 1) == NULL)
        return(0);
    /* prepare the FAB for a user file open */
    fab = cc$rms_fab;
    fab.fab$l_fop |= FAB$V_UFO;
    fab.fab$b_fac = FAB$M_GET;
    fab.fab$b_shr = FAB$M_SHRGET;
    fab.fab$l_fna = name;
    fab.fab$b_fns = strlen(name);
    status = sys$open(&fab);    /* open file & get channel number */
    if ((status&1) == 0)
        return(0);
    channel = (unsigned short)fab.fab$l_stv;
    inadr[0] = inadr[1] = (char *)0; /* just an address in P0 space */
    /*
     * Map the blocks of the file up to
     * the EOF block into virtual memory.
     */
    size = _tiffSizeProc(fd);
    status = sys$crmpsc(inadr, retadr, 0, SEC$M_EXPREG, 0,0,0, channel,
        TIFFhowmany(size,512), 0,0,0);
    if ((status&1) == 0){
        sys$dassgn(channel);
        return(0);
    }
    *pbase = (tdata_t) retadr[0];   /* starting virtual address */
    /*
     * Use the size of the file up to the
     * EOF mark for UNIX compatibility.
     */
    *psize = (toff_t) size;
    /* Record the section in the table */
    map_table[no_mapped].base = retadr[0];
    map_table[no_mapped].top = retadr[1];
    map_table[no_mapped].channel = channel;
    no_mapped++;

        return(1);
}

/*
 * This routine unmaps a section from the virtual address space of 
 * the process, but only if the base was the one returned from a
 * call to TIFFMapFileContents.
 */
static void
_tiffUnmapProc(thandle_t fd, tdata_t base, toff_t size)
{
    char *inadr[2];
    int i, j;
    
    /* Find the section in the table */
    for (i = 0;i < no_mapped; i++) {
        if (map_table[i].base == (char *) base) {
            /* Unmap the section */
            inadr[0] = (char *) base;
            inadr[1] = map_table[i].top;
            sys$deltva(inadr, 0, 0);
            sys$dassgn(map_table[i].channel);
            /* Remove this section from the list */
            for (j = i+1; j < no_mapped; j++)
                map_table[j-1] = map_table[j];
            no_mapped--;
            return;
        }
    }
}
#else /* !HAVE_MMAP */
static int
_tiffMapProc(thandle_t fd, tdata_t* pbase, toff_t* psize)
{
    return (0);
}

static void
_tiffUnmapProc(thandle_t fd, tdata_t base, toff_t size)
{
}
#endif /* !HAVE_MMAP */

/*
 * Open a TIFF file descriptor for read/writing.
 */
TIFF*
TIFFFdOpen(int fd, const char* name, const char* mode)
{
    TIFF* tif;

    tif = TIFFClientOpen(name, mode,
        (thandle_t) fd,
        _tiffReadProc, _tiffWriteProc, _tiffSeekProc, _tiffCloseProc,
        _tiffSizeProc, _tiffMapProc, _tiffUnmapProc);
    if (tif)
        tif->tif_fd = fd;
    return (tif);
}

/*
 * Open a TIFF file for read/writing.
 */
TIFF*
TIFFOpen(const char* name, const char* mode)
{
    static const char module[] = "TIFFOpen";
    int m, fd;

    m = _TIFFgetMode(mode, module);
    if (m == -1)
        return ((TIFF*)0);
        if (m&O_TRUNC){
                /*
         * There is a bug in open in VAXC. If you use
         * open w/ m=O_RDWR|O_CREAT|O_TRUNC the
         * wrong thing happens.  On the other hand
         * creat does the right thing.
                 */
                fd = creat((char *) /* bug in stdio.h */ name, 0666,
            "alq = 128", "deq = 64", "mbc = 32",
            "fop = tef");
    } else if (m&O_RDWR) {
        fd = open(name, m, 0666,
            "deq = 64", "mbc = 32", "fop = tef", "ctx = stm");
    } else
        fd = open(name, m, 0666, "mbc = 32", "ctx = stm");
    if (fd < 0) {
        TIFFError(module, "%s: Cannot open", name);
        return ((TIFF*)0);
    }
    return (TIFFFdOpen(fd, name, mode));
}

tdata_t
_TIFFmalloc(tsize_t s)
{
    return (malloc((size_t) s));
}

void
_TIFFfree(tdata_t p)
{
    free(p);
}

tdata_t
_TIFFrealloc(tdata_t p, tsize_t s)
{
    return (realloc(p, (size_t) s));
}

void
_TIFFmemset(tdata_t p, int v, tsize_t c)
{
    memset(p, v, (size_t) c);
}

void
_TIFFmemcpy(tdata_t d, const tdata_t s, tsize_t c)
{
    memcpy(d, s, (size_t) c);
}

int
_TIFFmemcmp(const tdata_t p1, const tdata_t p2, tsize_t c)
{
    return (memcmp(p1, p2, (size_t) c));
}

/*
 * On the VAX, we need to make those global, writable pointers
 * non-shareable, otherwise they would be made shareable by default.
 * On the AXP, this brain damage has been corrected. 
 * 
 * I (Karsten Spang, krs@kampsax.dk) have dug around in the GCC
 * manual and the GAS code and have come up with the following
 * construct, but I don't have GCC on my VAX, so it is untested.
 * Please tell me if it does not work.
 */

static void
vmsWarningHandler(const char* module, const char* fmt, va_list ap)
{
    if (module != NULL)
        fprintf(stderr, "%s: ", module);
    fprintf(stderr, "Warning, ");
    vfprintf(stderr, fmt, ap);
    fprintf(stderr, ".\n");
}

NOSHARE TIFFErrorHandler _TIFFwarningHandler = vmsWarningHandler
#if defined(VAX) && defined(__GNUC__)
asm("_$$PsectAttributes_NOSHR$$_TIFFwarningHandler")
#endif
;

static void
vmsErrorHandler(const char* module, const char* fmt, va_list ap)
{
    if (module != NULL)
        fprintf(stderr, "%s: ", module);
    vfprintf(stderr, fmt, ap);
    fprintf(stderr, ".\n");
}

NOSHARE TIFFErrorHandler _TIFFerrorHandler = vmsErrorHandler
#if defined(VAX) && defined(__GNUC__)
asm("_$$PsectAttributes_NOSHR$$_TIFFerrorHandler")
#endif
;


#if !HAVE_IEEEFP
/* IEEE floting point handling */

typedef struct ieeedouble {
    u_long  mant2;          /* fix NDR: full 8-byte swap */
    u_long  mant    : 20,
        exp : 11,
        sign    : 1;
} ieeedouble;
typedef struct ieeefloat {
    u_long  mant    : 23,
        exp : 8,
        sign    : 1;
} ieeefloat;

/* 
 * NB: These are D_FLOAT's, not G_FLOAT's. A G_FLOAT is
 *  simply a reverse-IEEE float/double.
 */

typedef struct {
    u_long  mant1   : 7,
        exp : 8,
        sign    : 1,
        mant2   : 16,
        mant3   : 16,
        mant4   : 16;
} nativedouble;
typedef struct {
    u_long  mant1   : 7,
        exp : 8,
        sign    : 1,
        mant2   : 16;
} nativefloat;

typedef union {
    ieeedouble  ieee;
    nativedouble    native;
    char        b[8];
    uint32      l[2];
    double      d;
} double_t;

typedef union {
    ieeefloat   ieee;
    nativefloat native;
    char        b[4];
    uint32      l;
    float       f;
} float_t;

#if defined(VAXC) || defined(DECC)
#pragma inline(ieeetod,dtoieee)
#endif

/*
 * Convert an IEEE double precision number to native double precision.
 * The source is contained in two longwords, the second holding the sign,
 * exponent and the higher order bits of the mantissa, and the first
 * holding the rest of the mantissa as follows:
 * (Note: It is assumed that the number has been eight-byte swapped to
 * LSB first.)
 * 
 * First longword:
 *  32 least significant bits of mantissa
 * Second longword:
 *  0-19:   20 most significant bits of mantissa
 *  20-30:  exponent
 *  31: sign
 * The exponent is stored as excess 1023.
 * The most significant bit of the mantissa is implied 1, and not stored.
 * If the exponent and mantissa are zero, the number is zero.
 * If the exponent is 0 (i.e. -1023) and the mantissa is non-zero, it is an
 * unnormalized number with the most significant bit NOT implied.
 * If the exponent is 2047, the number is invalid, in case the mantissa is zero,
 * this means overflow (+/- depending of the sign bit), otherwise
 * it simply means invalid number.
 * 
 * If the number is too large for the machine or was specified as overflow, 
 * +/-HUGE_VAL is returned.
 */
INLINE static void
ieeetod(double *dp)
{
    double_t source;
    long sign,exp,mant;
    double dmant;

    source.ieee = ((double_t*)dp)->ieee;
    sign = source.ieee.sign;
    exp = source.ieee.exp;
    mant = source.ieee.mant;

    if (exp == 2047) {
        if (mant)           /* Not a Number (NAN) */
            *dp = HUGE_VAL;
        else                /* +/- infinity */
            *dp = (sign ? -HUGE_VAL : HUGE_VAL);
        return;
    }
    if (!exp) {
        if (!(mant || source.ieee.mant2)) { /* zero */
            *dp=0;
            return;
        } else {            /* Unnormalized number */
            /* NB: not -1023, the 1 bit is not implied */
            exp= -1022;
        }
    } else {
        mant |= 1<<20;
        exp -= 1023;
    }
    dmant = (((double) mant) +
        ((double) source.ieee.mant2) / (((double) (1<<16)) *
        ((double) (1<<16)))) / (double) (1<<20);
    dmant = ldexp(dmant, exp);
    if (sign)
        dmant= -dmant;
    *dp = dmant;
}

INLINE static void
dtoieee(double *dp)
{
    double_t num;
    double x;
    int exp;

    num.d = *dp;
    if (!num.d) {           /* Zero is just binary all zeros */
        num.l[0] = num.l[1] = 0;
        return;
    }

    if (num.d < 0) {        /* Sign is encoded separately */
        num.d = -num.d;
        num.ieee.sign = 1;
    } else {
        num.ieee.sign = 0;
    }

    /* Now separate the absolute value into mantissa and exponent */
    x = frexp(num.d, &exp);

    /*
     * Handle cases where the value is outside the
     * range for IEEE floating point numbers. 
     * (Overflow cannot happen on a VAX, but underflow
     * can happen for G float.)
     */
    if (exp < -1022) {      /* Unnormalized number */
        x = ldexp(x, -1023-exp);
        exp = 0;
    } else if (exp > 1023) {    /* +/- infinity */
        x = 0;
        exp = 2047;
    } else {            /* Get rid of most significant bit */
        x *= 2;
        x -= 1;
        exp += 1022; /* fix NDR: 1.0 -> x=0.5, exp=1 -> ieee.exp = 1023 */
    }
    num.ieee.exp = exp;

    x *= (double) (1<<20);
    num.ieee.mant = (long) x;
    x -= (double) num.ieee.mant;
    num.ieee.mant2 = (long) (x*((double) (1<<16)*(double) (1<<16)));

    if (!(num.ieee.mant || num.ieee.exp || num.ieee.mant2)) {
        /* Avoid negative zero */
        num.ieee.sign = 0;
    }
    ((double_t*)dp)->ieee = num.ieee;
}

/*
 * Beware, these do not handle over/under-flow
 * during conversion from ieee to native format.
 */
#define NATIVE2IEEEFLOAT(fp) { \
    float_t t; \
    if (t.ieee.exp = (fp)->native.exp) \
    t.ieee.exp += -129 + 127; \
    t.ieee.sign = (fp)->native.sign; \
    t.ieee.mant = ((fp)->native.mant1<<16)|(fp)->native.mant2; \
    *(fp) = t; \
}
#define IEEEFLOAT2NATIVE(fp) { \
    float_t t; int v = (fp)->ieee.exp; \
    if (v) v += -127 + 129;     /* alter bias of exponent */\
    t.native.exp = v;           /* implicit truncation of exponent */\
    t.native.sign = (fp)->ieee.sign; \
    v = (fp)->ieee.mant; \
    t.native.mant1 = v >> 16; \
    t.native.mant2 = v;\
    *(fp) = t; \
}

#define IEEEDOUBLE2NATIVE(dp) ieeetod(dp)

#define NATIVE2IEEEDOUBLE(dp) dtoieee(dp)


/*
 * These unions are used during floating point
 * conversions.  The above macros define the
 * conversion operations.
 */
void
TIFFCvtIEEEFloatToNative(TIFF* tif, u_int n, float* f)
{
    float_t* fp = (float_t*) f;

    while (n-- > 0) {
        IEEEFLOAT2NATIVE(fp);
        fp++;
    }
}

void
TIFFCvtNativeToIEEEFloat(TIFF* tif, u_int n, float* f)
{
    float_t* fp = (float_t*) f;

    while (n-- > 0) {
        NATIVE2IEEEFLOAT(fp);
        fp++;
    }
}
void
TIFFCvtIEEEDoubleToNative(TIFF* tif, u_int n, double* f)
{
    double_t* fp = (double_t*) f;

    while (n-- > 0) {
        IEEEDOUBLE2NATIVE(fp);
        fp++;
    }
}

void
TIFFCvtNativeToIEEEDouble(TIFF* tif, u_int n, double* f)
{
    double_t* fp = (double_t*) f;

    while (n-- > 0) {
        NATIVE2IEEEDOUBLE(fp);
        fp++;
    }
}
#endif
