/*
    GETPUTEA.C  Routines to read and write extended attributes.
                Compatible with OS/2 version 1.2.  Microsoft or
                IBM PTK header files not required.  Will need 
                modification for OS/2 2.0.

    Copyright (C) 1989 Ziff Davis Communications
    PC Magazine * Ray Duncan, December 1989
*/

#include <stdio.h>
#include <string.h>
#include <memory.h>
#include <malloc.h>
                                        // EA predefined value types
#define EAT_BINARY      0x0fffe         // Length-preceeded binary
#define EAT_ASCII       0x0fffd         // Length-preceeded ASCII
#define EAT_BITMAP      0x0fffb         // Length-preceeded bitmap
#define EAT_METAFILE    0x0fffa         // Metafile
#define EAT_ICON        0x0fff9         // Length-preceeded icon
#define EAT_EA          0x0ffee         // ASCIIZ name of associated EA
#define EAT_MVMT        0x0ffdf         // Multi-value multi-type
#define EAT_MVST        0x0ffde         // Multi-value single-type
#define EAT_ASN1        0x0ffdd         // ASN.1 field

#define MAXPATHNAME     260             // max length of pathname
#define MAXFILENAME     255             // max length of filename
#define FEALISTSIZE     1024            // arbitrary buffer size
#define GEALISTSIZE     260             // arbitrary buffer size

#define API unsigned extern far pascal  // OS/2 API function prototypes 
API DosQFileInfo(unsigned, unsigned, void far *, unsigned);
API DosSetFileInfo(unsigned, unsigned, void far *, unsigned);

struct _EA {                            // extended attribute header 
        unsigned char flags;            // critical flag etc.
        unsigned char nsize;            // length of EA name (without null)
        unsigned vsize;                 // total size of EA value
        char name[1]; } ;               // EA name and value begin here

struct _EAval {                         // extended attribute value 
        unsigned type;                  // EA value type
        unsigned size;                  // length of EA variable data
        char data[1]; } ;               // actual data begins here

struct _FEAList {                       // receives extended attributes
        unsigned long size;             // total size of structure
        char data[1]; } ;               // extended attributes begin here

struct _GEA {                           // extended attribute target name
        unsigned char size;             // length of name
        char name[1]; } ;               // actual name begins here

struct _GEAList {                       // holds names of EAs to get
        unsigned long size;             // total size of structure
        struct _GEA GEA; } ;            // name length and name text

struct _EAOP {                          // used by all EA functions
        void far *pGEAList;             // pointer to GEAList structure         
        void far *pFEAList;             // pointer to FEAList structure
        unsigned long oError; } ;       // offset of error, if any

/*
    putEA: constructs and writes an extended attribute for a previously 
    opened file.  Called with the handle of the file, the extended 
    attribute name, the EA value type, and the address and length of 
    the EA value field data.  Returns FALSE if extended attribute was 
    written successfully. Returns -1 or OS/2 errcode if function failed.
*/
int putEA(int handle, char * eaname, 
          void * eavalue, unsigned eavalsize, unsigned eatype)
{
    struct _FEAList *pFEAList;          // misc scratch variables
    struct _EA      *pEA;               // and pointers
    struct _EAval   *pEAval;
    struct _EAOP    EAOP;
    int errcode;

    pFEAList = malloc(FEALISTSIZE);     // allocate buffer for FEAList

    if(pFEAList == NULL) return(-1);    // bail out if no heap available    

    EAOP.pFEAList = pFEAList;           // initialize contents of 
    EAOP.pGEAList = NULL;               // EAOP pointer structure
    EAOP.oError   = NULL;

    (char *) pEA = pFEAList->data;      // point to EA header field     

    pEA->flags = 0;                     // construct header portion 
    pEA->nsize = strlen(eaname);        // of extended attribute
    pEA->vsize = eavalsize + 4;
    strcpy(pEA->name, eaname);

    (char *) pEAval =                   // calculate address of 
        pEA->name + pEA->nsize + 1;     // EA's value field

    pEAval->type = eatype;              // construct value portion
    pEAval->size = eavalsize;           // of extended attribute
    memcpy(pEAval->data, eavalue, eavalsize);

    pFEAList->size =                    // calculate size of entire
        pEA->nsize + pEA->vsize + 9;    // FEAList structure

    errcode =                           // set extended attributes
        DosSetFileInfo(handle,          // file handle
                       2,               // info level 2 = set EAs
                       &EAOP,           // EAOP structure holds pointer 
                       sizeof(EAOP));   // to FEAList containing EAs

    free(pFEAList);                     // release working buffer 
    return(errcode);                    // return DosSetFileInfo status
}

/*
    getEA: fetches an extended attribute for a previously opened file.  
    Returns FALSE if the extended attribute does not exist; otherwise, 
    returns a pointer to the extended attribute value in a chunk of 
    heap memory.  The EA value is in the form of the EA type indicator, 
    followed by the length of variable data, followed by the data itself.  
    The exact format of the EA value is given by the structure _EAval.
    The caller must be sure to free the memory holding the extended 
    attribute value after the value has been used or copied elsewhere.
*/
struct _EAval * getEA(int handle, char * eaname)
{
    struct _FEAList *pFEAList = NULL;   // misc scratch variables
    struct _GEAList *pGEAList = NULL;   // and pointers
    struct _EA      *pEA = NULL;
    struct _EAval   *pEAval = NULL;
    struct _EAOP    EAOP;

    pFEAList = malloc(FEALISTSIZE);     // allocate buffers
    pGEAList = malloc(GEALISTSIZE);             

    if((pFEAList == NULL) || (pGEAList == NULL))
        goto errexit;                   // bail out if no heap

    EAOP.pFEAList = pFEAList;           // initialize contents of 
    EAOP.pGEAList = pGEAList;           // EAOP pointer structure
    EAOP.oError   = NULL;

    strcpy(pGEAList->GEA.name, eaname); // fill in EA name & length
    pGEAList->GEA.size = strlen(eaname);

    pFEAList->size = FEALISTSIZE;       // fill in structure sizes
    pGEAList->size = pGEAList->GEA.size + 6;    

    if(DosQFileInfo(handle,             // get extended attribute
                    3,                  // info level 3 = get EA
                    &EAOP,              // EAOP struct holds pointers
                    sizeof(EAOP)))      // to GEAList and FEAList
        goto errexit;                   // exit if API function failed

    (char *) pEA = pFEAList->data;      // point to EA header field

    if(pEA->vsize == 0) goto errexit;   // exit if no EA value present

    if((pEAval = malloc(pEA->vsize))    // allocate space for EA value
        == NULL) goto errexit;          // exit if heap is full

                                        // copy EA value to heap
    memcpy(pEAval, pEA->name + pEA->nsize + 1, pEA->vsize);

    free(pFEAList);                     // release working buffers
    free(pGEAList);
    return(pEAval);                     // return pointer to EA value

errexit:                                // common exit point for errors
    if(pFEAList != NULL) free(pFEAList);
    if(pGEAList != NULL) free(pGEAList);
    return(NULL);                       // NULL pointer indicates error
}

