
/*
 *@@sourcefile eas.c:
 *      contains helper functions for handling Extended Attributes.
 *      See explanations below.
 *
 *      Function prefixes (new with V0.81):
 *      --  ea*         EA helper functions
 *
 *      This file is new with V0.81 and contains all the EA functions
 *      that were in helpers.c previously.
 *
 *@@include #include <os2.h>
 *@@include #include "eas.h"
 */

/*
 *      Most of the code in this file dealing with Extended Attributes is based
 *      on code (w) by Chris Hanson (cph@zurich.ai.mit.edu).
 *      Copyright (c) 1995 Massachusetts Institute of Technology.
 *
 *      The original code is available as EALIB.ZIP at Hobbes.
 *
 *      This material was developed by the Scheme project at the Massachusetts
 *      Institute of Technology, Department of Electrical Engineering and
 *      Computer Science.  Permission to copy this software, to redistribute
 *      it, and to use it for any purpose is granted, subject to the following
 *      restrictions and understandings.
 *
 *      1. Any copy made of this software must include this copyright notice
 *      in full.
 *
 *      2. Users of this software agree to make their best efforts (a) to
 *      return to the MIT Scheme project any improvements or extensions that
 *      they make, so that these may be included in future releases; and (b)
 *      to inform MIT of noteworthy uses of this software.
 *
 *      3. All materials developed as a consequence of the use of this
 *      software shall duly acknowledge such use, in accordance with the usual
 *      standards of acknowledging credit in academic research.
 *
 *      4. MIT has made no warrantee or representation that the operation of
 *      this software will be error-free, and MIT is under no obligation to
 *      provide any services, by way of maintenance, update, or otherwise.
 *
 *      5. In conjunction with products arising from the use of this material,
 *      there shall be no use of the name of the Massachusetts Institute of
 *      Technology nor of any adaptation thereof in any advertising,
 *      promotional, or sales literature without prior written consent from
 *      MIT in each case.
 *
 *      This file Copyright (C) 1997-99 Ulrich M”ller,
 *                                      Massachusetts Institute of Technology.
 *      This file is part of the XFolder source package.
 *      XFolder is free software; you can redistribute it and/or modify
 *      it under the terms of the GNU General Public License as published
 *      by the Free Software Foundation, in version 2 as it comes in the
 *      "COPYING" file of the XFolder main distribution.
 *      This program is distributed in the hope that it will be useful,
 *      but WITHOUT ANY WARRANTY; without even the implied warranty of
 *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *      GNU General Public License for more details.
 */

#define INCL_DOS
#define INCL_DOSERRORS
#include <os2.h>

#include <stdlib.h>
#include <string.h>

#include "eas.h"

// #define _PMPRINTF_
#include "pmprintf.h"

/********************************************************************
 *                                                                  *
 *   Extended Attribute handling                                    *
 *                                                                  *
 ********************************************************************/

#define EA_BINDING_FLAGS(binding) ((binding)->bFlags)
#define EA_BINDING_NAME_LENGTH(binding) ((binding)->bNameLength)
#define EA_BINDING_VALUE_LENGTH(binding) ((binding)->usValueLength)
#define EA_BINDING_NAME(binding) ((binding)->pszName)
#define EA_BINDING_VALUE(binding) ((binding)->pszValue)

#define EA_LIST_BINDING(list) ((list)->binding)
#define EA_LIST_NEXT(list) ((list)->next)

// forward declarations to helper funcs at bottom
static PEALIST      ReadEAList(ULONG, PVOID);
static EABINDING *  ReadEAByIndex(ULONG, PVOID, ULONG);
static EABINDING *  ReadEAByName(ULONG, PVOID, PSZ);
static PDENA2       ReadDenaByIndex(ULONG, PVOID, ULONG);
static PEABINDING   GetEAValue(ULONG, PVOID, PDENA2);
static void         SetupQueryEAInfo(PDENA2, PEAOP2);
static PEABINDING   ConvertFeal2Binding(PFEA2LIST);
static void         WriteEAList(ULONG, PVOID, PEALIST);
static void         WriteEA(ULONG, PVOID, PEABINDING);
static PFEA2LIST    ConvertBinding2Feal(PEABINDING);

/*
 *@@ eaFreeBinding:
 *      deallocate EA binding memory that was generated
 *      by the ea...Read... procedures below. These procs
 *      assume that "malloc" was used for allocation and
 *      that the "name" and "value" fields of each binding
 *      were also allocated using "malloc". "value" may also
 *      be NULL.
 */

void eaFreeBinding(PEABINDING binding)
{
    free(EA_BINDING_NAME(binding));
    if ((EA_BINDING_VALUE(binding)) != 0)
        free(EA_BINDING_VALUE(binding));
    free(binding);
}

/*
 *@@ eaFreeList:
 *      like eaFreeBinding, but for an EA binding list.
 */

void eaFreeList(PEALIST list)
{
    while (list != 0)
    {
        PEALIST next = (EA_LIST_NEXT (list));
        eaFreeBinding(EA_LIST_BINDING (list));
        free(list);
        list = next;
    }
}

/*
 * All of the following functions come in two flavors:
 *
 *      eaHFile*    operate on an open file handle.
 *
 *      eaPath*     operate on any file specified by its
 *                  filename, which may be fully qualified.
 */

/*
 * ------ READ EXTENDED ATTRIBUTES
 */

/*
 *@@ eaPathQueryTotalSize::
 *      returns the total size of all EAs for a given file.
 *      (W) Ulrich M”ller.
 */

ULONG eaPathQueryTotalSize(PSZ pszPath)
{
    APIRET  arc;
    ULONG   ulTotalEASize = 0;
    CHAR    pchPath;
    // HDIR    hdir;
    // ULONG ulCount = 1;
    FILEFINDBUF4   ffb4;

    _Pmpf(("eaPathQueryTotalSize %s", pszPath));

    arc = DosQueryPathInfo(pszPath,
                       // &hdir,
                       // FILE_ARCHIVED | FILE_HIDDEN | FILE_SYSTEM | FILE_READONLY | FILE_DIRECTORY,
                       FIL_QUERYEASIZE,
                       &ffb4,
                       sizeof(FILEFINDBUF4));

    if (arc == NO_ERROR)
    {
        // CHAR szFile[CCHMAXPATH];
        // PBYTE pbBuffer = malloc(ffb4.cbList);
        BYTE abBuf[2000];
        LONG lCount = 0;
        PDENA2 pdena2;

        lCount = -1;

        arc = DosEnumAttribute(ENUMEA_REFTYPE_PATH,
                                      pszPath,
                                      1,
                                      abBuf,
                                      sizeof(abBuf),
                                      (PULONG)&lCount,
                                      ENUMEA_LEVEL_NO_VALUE);
            // ulCount now contains the EA count

        pdena2 = (PDENA2)abBuf;

        _Pmpf(("  %s: arc = %d, count = %d", pszPath, arc, lCount));

        if (lCount > 0) {
            ulTotalEASize = pdena2->cbName + 8;

            while (lCount > 0) {
                ulTotalEASize += (pdena2->cbValue + sizeof(DENA2));
                lCount--;
                pdena2 = (PDENA2) (((PBYTE) pdena2) +
                              pdena2->oNextEntryOffset);

            }
        }
    }

    _Pmpf(("    %s: total %d", pszPath, ulTotalEASize));

    return (ulTotalEASize);
}

/*
 *@@ eaPathReadAll:
 *      reads all of the extended attributes into an EALIST.
 *      Returns NULL if no EAs were found.
 *      The returned list should be freed using eaFreeList.
 */

PEALIST eaPathReadAll(PSZ path)
{
    return (ReadEAList(ENUMEA_REFTYPE_PATH, path));
}

/*
 *@@ eaHFileReadAll:
 *      like eaPathReadAll, but for an open file handle.
 */

PEALIST eaHFileReadAll(HFILE hfile)
{
    return (ReadEAList(ENUMEA_REFTYPE_FHANDLE, (&hfile)));
}

/*
 *@@ eaPathReadOneByIndex:
 *      returns one EA specified by a given index, counting
 *      from 1. Returns NULL if the specified index was not
 *      found, either because the file has no EAs at all or
 *      the index is too large.
 *      The returned binding should be freed using eaFreeBinding.
 */

PEABINDING eaPathReadOneByIndex(PSZ path, ULONG index)
{
    return (ReadEAByIndex(ENUMEA_REFTYPE_PATH, path, index));
}

/*
 *@@ eaHFileReadOneByIndex:
 *      like eaPathReadOneByIndex, but for an open file handle.
 */

PEABINDING eaHFileReadOneByIndex(HFILE hfile, ULONG index)
{
    return (ReadEAByIndex(ENUMEA_REFTYPE_FHANDLE, (&hfile), index));
}

/*
 *@@ eaPathReadOneByName:
 *      returns one EA specified by the given EA name (e.g.
 *      ".LONGNAME"). Returns NULL if not found.
 *      The returned binding should be freed using eaFreeBinding.
 */

PEABINDING eaPathReadOneByName(PSZ path, PSZ name)
{
    return (ReadEAByName(ENUMEA_REFTYPE_PATH, path, name));
}

/*
 *@@ eaHFileReadOneByName:
 *      like eaPathReadOneByName, but for an open file handle.
 */

PEABINDING eaHFileReadOneByName(HFILE hfile, PSZ name)
{
    return (ReadEAByName(ENUMEA_REFTYPE_FHANDLE, (&hfile), name));
}

/*
 * ------ WRITE EXTENDED ATTRIBUTES
 */

/*
 *@@ eaPathWriteAll:
 *      writes a list of EAs to a given file. These EAs
 *      are added to possibly existing EAs on the file.
 *      A given EA is deleted if its usValueLength field
 *      is 0; in that case, the value field may also be NULL.
 */

void eaPathWriteAll(PSZ path, PEALIST list)
{
    WriteEAList(ENUMEA_REFTYPE_PATH, path, list);
}

/*
 *@@ eaHFileWriteAll:
 *      like eaPathWriteAll, but for an open file handle.
 */

void eaHFileWriteAll(HFILE hfile, PEALIST list)
{
    WriteEAList(ENUMEA_REFTYPE_FHANDLE, (&hfile), list);
}

/*
 *@@ eaPathWriteOne:
 *      adds one EA to a given file.
 *      A given EA is deleted if its usValueLength field
 *      is 0; in that case, the value field may also be NULL.
 */

void eaPathWriteOne(PSZ path, PEABINDING binding)
{
    WriteEA(ENUMEA_REFTYPE_PATH, path, binding);
}

/*
 *@@ eaHFileWriteOne:
 *      like eaPathWriteOne, but for an open file handle.
 */

void eaHFileWriteOne(HFILE hfile, PEABINDING binding)
{
    WriteEA(ENUMEA_REFTYPE_FHANDLE, (&hfile), binding);
}

/********************************************************************
 *                                                                  *
 *   EA helper funcs                                                *
 *                                                                  *
 ********************************************************************/

static PEALIST ReadEAList(ULONG type, PVOID pfile)
{
    ULONG index = 1;
    PEALIST head = 0;
    PEALIST tail = 0;

    while (1)
    {
        PEABINDING binding = (ReadEAByIndex(type, pfile, index));
        if (binding == 0)
            break;
        {
            PEALIST list = (malloc(sizeof (EALIST)));
            (EA_LIST_BINDING (list)) = binding;
            (EA_LIST_NEXT (list)) = 0;
            if (head == 0)
                head = list;
            else
                (EA_LIST_NEXT (tail)) = list;
            tail = list;
        }
        index += 1;
    }
    return (head);
}

static PEABINDING ReadEAByIndex(ULONG type, PVOID pfile, ULONG index)
{
    PDENA2 dena = (ReadDenaByIndex(type, pfile, index));
    return ((dena == 0)
                    ? 0
                    : (GetEAValue(type, pfile, dena)));
}

static PEABINDING ReadEAByName(ULONG type, PVOID pfile, PSZ name)
{
    ULONG index = 1;
    while (1)
    {
        PDENA2 dena = ReadDenaByIndex(type, pfile, index);
        if (dena == 0)
            return (NULL);
        if ((strcmp(name, (dena->szName))) == 0)
            return (GetEAValue(type, pfile, dena));
        free(dena);
        index += 1;
    }
}

static PDENA2 ReadDenaByIndex(ULONG type, PVOID pfile, ULONG index)
{
    ULONG count = 1;
    PDENA2 dena = (malloc(500)); // 500 is magic -- IBM doesn't explain.
    APIRET arc = DosEnumAttribute(type, pfile, index, dena, 500, (&count),
                     ENUMEA_LEVEL_NO_VALUE);
    if (count == 0)
    {
        free(dena);
        return (0);
    }
    else
        return (dena);
}

static PEABINDING GetEAValue(ULONG type, PVOID pfile, PDENA2 dena)
{
    ULONG level = FIL_QUERYEASFROMLIST;
    EAOP2 eaop;
    ULONG size = (sizeof(eaop));
    APIRET arc = NO_ERROR;
    SetupQueryEAInfo(dena, (&eaop));
    if (type == ENUMEA_REFTYPE_FHANDLE)
        arc = DosQueryFileInfo((* ((PHFILE) pfile)), level, (&eaop), size);
    else
        arc = DosQueryPathInfo(pfile, level, (&eaop), size);
    free(eaop.fpGEA2List);
    return (ConvertFeal2Binding(eaop.fpFEA2List));
}

static void SetupQueryEAInfo(PDENA2 dena, PEAOP2 eaop)
{
    unsigned int geal_size = ((sizeof (GEA2LIST)) + (dena->cbName));
    unsigned int feal_size
                 = ((sizeof (FEA2LIST)) + (dena->cbName) + (dena->cbValue));
    (eaop->fpGEA2List) = (malloc(geal_size));
    ((eaop->fpGEA2List)->cbList) = geal_size;
    (eaop->fpFEA2List) = (malloc(feal_size));
    ((eaop->fpFEA2List)->cbList) = feal_size;
    (eaop->oError) = 0;
    {
        PGEA2 gea = (&(((eaop->fpGEA2List)->list) [0]));
        (gea->oNextEntryOffset) = 0;
        (gea->cbName) = (dena->cbName);
        strcpy ((gea->szName), (dena->szName));
    }
    free(dena);
}

static PEABINDING ConvertFeal2Binding(PFEA2LIST feal)
{
    PFEA2 fea = (&((feal->list) [0]));
    PEABINDING binding = (malloc(sizeof (EABINDING)));
    (EA_BINDING_FLAGS (binding)) = (fea->fEA);
    (EA_BINDING_NAME_LENGTH (binding)) = (fea->cbName);
    (EA_BINDING_VALUE_LENGTH (binding)) = (fea->cbValue);
    (EA_BINDING_NAME (binding)) = (malloc((fea->cbName) + 1));
    strcpy ((EA_BINDING_NAME (binding)), (fea->szName));
    (EA_BINDING_VALUE (binding)) = (malloc(fea->cbValue));
    memcpy ((EA_BINDING_VALUE (binding)),
        (&((fea->szName) [(fea->cbName) + 1])),
        (fea->cbValue));
    free(feal);
    return (binding);
}

static void WriteEAList(ULONG type, PVOID pfile, PEALIST list)
{
    while (list != 0)
    {
        WriteEA(type, pfile, (EA_LIST_BINDING (list)));
        list = (EA_LIST_NEXT (list));
    }
}

static void WriteEA(ULONG type, PVOID pfile, PEABINDING binding)
{
    ULONG level = FIL_QUERYEASIZE;
    EAOP2 eaop;
    ULONG size = (sizeof (eaop));
    APIRET arc = NO_ERROR;

    (eaop.fpGEA2List) = 0;
    (eaop.fpFEA2List) = (ConvertBinding2Feal(binding));
    (eaop.oError) = 0;
    if (type == ENUMEA_REFTYPE_FHANDLE)
        arc = DosSetFileInfo((* ((PHFILE) pfile)), level, (&eaop), size);
    else
        arc = DosSetPathInfo(pfile, level, (&eaop), size, DSPI_WRTTHRU);
    free(eaop.fpFEA2List);
}

static PFEA2LIST ConvertBinding2Feal(PEABINDING binding)
{
    unsigned int feal_size
              = ((sizeof (FEA2LIST))
                 + (EA_BINDING_NAME_LENGTH (binding))
                 + (EA_BINDING_VALUE_LENGTH (binding)));
    PFEA2LIST feal = (malloc(feal_size));
    PFEA2 fea = (&((feal->list) [0]));
    (feal->cbList) = feal_size;
    (fea->oNextEntryOffset) = 0;
    (fea->fEA) = (EA_BINDING_FLAGS (binding));
    (fea->cbName) = (EA_BINDING_NAME_LENGTH (binding));
    (fea->cbValue) = (EA_BINDING_VALUE_LENGTH (binding));
    strcpy ((fea->szName), (EA_BINDING_NAME (binding)));
    if ((EA_BINDING_VALUE (binding)) != 0)
        memcpy ((&((fea->szName) [(fea->cbName) + 1])),
            (EA_BINDING_VALUE (binding)),
            (fea->cbValue));
    return (feal);
}


