/* *@@sourcefile eah.c: * contains helper functions for handling Extended Attributes. * See explanations below. * * Function prefixes (new with V0.81): * -- ea* EA helper functions * * Usage: All OS/2 programs. * * This file is new with V0.81 and contains all the EA functions * that were in helpers.c previously. * * Most of the functions in here come in pairs: one for working * on EAs based on a path (eaPath*), one for open HFILEs (eaHFile*). * Most functions return structures called EABINDING which should * be freed again later. * * Example: + PEABINDING peab = eaPathReadOneByName("C:\Desktop", ".LONGNAME"); + if (peab) + PSZ pszLongName = peab->pszValue; + eaFreeBinding(peab); * * Note: Version numbering in this file relates to XWorkplace version * numbering. * *@@header "helpers\eah.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. * * Copyright (C) 1995 Massachusetts Institute of Technology. * Copyright (C) 1997-2002 Ulrich M”ller. * This file is part of the "XWorkplace helpers" source package. * This 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 XWorkplace 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 OS2EMX_PLAIN_CHAR // this is needed for "os2emx.h"; if this is defined, // emx will define PSZ as _signed_ char, otherwise // as unsigned char #define INCL_DOSFILEMGR #define INCL_DOSERRORS #include #include #include #include "setup.h" // code generation and debugging options #include "helpers\eah.h" #pragma hdrstop /* *@@category: Helpers\Control program helpers\Extended attributes * See eah.c. */ /******************************************************************** * * 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)->peab) #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 APIRET WriteEAList(ULONG, PVOID, PEALIST); STATIC APIRET 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 "pszName" and "pszValue" fields of each binding * were also allocated using "malloc". "pszValue" may also * be NULL. * *@@changed V0.9.0 [umoeller]: added check for (binding != NULL) */ void eaFreeBinding(PEABINDING peab) { if (peab) { free(EA_BINDING_NAME(peab)); if ((EA_BINDING_VALUE(peab)) != 0) free(EA_BINDING_VALUE(peab)); free(peab); } } /* *@@ eaFreeList: * like eaFreeBinding, but for an EA binding list. * This calls eaFreeBinding for each list item. * *@@changed V0.9.0 [umoeller]: added check for (list != NULL) */ void eaFreeList(PEALIST list) { if (list) { while (list != 0) { PEALIST next = (EA_LIST_NEXT (list)); eaFreeBinding(EA_LIST_BINDING (list)); free(list); list = next; } } } /* ****************************************************************** * * Read-EA functions * ********************************************************************/ /* * 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. */ /* *@@ eaPathQueryTotalSize: * returns the total size of all EAs for a given file. * This does not use the other EA functions, but * accesses the EAs directly, so this is a lot quicker * if you only want the total EA size without accessing * the EA data. */ ULONG eaPathQueryTotalSize(const char *pcszPath) { APIRET arc; ULONG ulTotalEASize = 0; FILEFINDBUF4 ffb4; if (!(arc = DosQueryPathInfo((PSZ)pcszPath, FIL_QUERYEASIZE, &ffb4, sizeof(FILEFINDBUF4)))) { BYTE abBuf[2000]; LONG lCount = 0; PDENA2 pdena2; lCount = -1; if (!(arc = DosEnumAttribute(ENUMEA_REFTYPE_PATH, (PSZ)pcszPath, 1, abBuf, sizeof(abBuf), (PULONG)&lCount, ENUMEA_LEVEL_NO_VALUE))) { // ulCount now contains the EA count pdena2 = (PDENA2)abBuf; if (lCount > 0) { ulTotalEASize = pdena2->cbName + 8; while (lCount > 0) { ulTotalEASize += (pdena2->cbValue + sizeof(DENA2)); lCount--; pdena2 = (PDENA2) (((PBYTE) pdena2) + pdena2->oNextEntryOffset); } } } } 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(const char *pcszPath) { return (ReadEAList(ENUMEA_REFTYPE_PATH, (PSZ)pcszPath)); } /* *@@ 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(const char *pcszPath, ULONG index) { return (ReadEAByIndex(ENUMEA_REFTYPE_PATH, (PSZ)pcszPath, 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(const char *pcszPath, const char *pcszEAName) { return (ReadEAByName(ENUMEA_REFTYPE_PATH, (PSZ)pcszPath, (PSZ)pcszEAName)); } /* *@@ eaHFileReadOneByName: * like eaPathReadOneByName, but for an open file handle. */ PEABINDING eaHFileReadOneByName(HFILE hfile, const char *pcszEAName) { return (ReadEAByName(ENUMEA_REFTYPE_FHANDLE, (&hfile), (PSZ)pcszEAName)); } /* ****************************************************************** * * Write-EA functions * ********************************************************************/ /* *@@ eaPathWriteAll: * writes a list of EAs to a given file. These EAs * are added to possibly existing EAs on the file; * existing EAs will be overwritten. * * A given EA is deleted if its EABINDING.usValueLength * field is 0; only in that case, the EABINDING.value * field may also be NULL. * *@@changed V0.9.7 (2000-11-30) [umoeller]: now returning APIRET */ APIRET eaPathWriteAll(const char *pcszPath, PEALIST list) { return (WriteEAList(ENUMEA_REFTYPE_PATH, (PSZ)pcszPath, list)); } /* *@@ eaHFileWriteAll: * like eaPathWriteAll, but for an open file handle. * *@@changed V0.9.7 (2000-11-30) [umoeller]: now returning APIRET */ APIRET eaHFileWriteAll(HFILE hfile, PEALIST list) { return (WriteEAList(ENUMEA_REFTYPE_FHANDLE, (&hfile), list)); } /* *@@ eaPathWriteOne: * adds one EA to a given file. If the EA exists * alredy, it is overwritten. * * A given EA is deleted if its EABINDING.usValueLength * field is 0; only in that case, the EABINDING.pszValue * field may also be NULL. * * To delete an EA, you may also use eaPathDeleteOne. * *@@changed V0.9.7 (2000-11-30) [umoeller]: now returning APIRET */ APIRET eaPathWriteOne(const char *pcszPath, PEABINDING peab) { return (WriteEA(ENUMEA_REFTYPE_PATH, (PSZ)pcszPath, peab)); } /* *@@ eaHFileWriteOne: * like eaPathWriteOne, but for an open file handle. * *@@changed V0.9.7 (2000-11-30) [umoeller]: now returning APIRET */ APIRET eaHFileWriteOne(HFILE hfile, PEABINDING peab) { return (WriteEA(ENUMEA_REFTYPE_FHANDLE, (&hfile), peab)); } /* *@@ eaPathDeleteOne: * this deletes one EA by constructing a temporary * empty EABINDING for pszEAName and calling * eaPathWriteOne. * *@@added V0.9.0 [umoeller] *@@changed V0.9.7 (2000-11-30) [umoeller]: now returning APIRET */ APIRET eaPathDeleteOne(const char *pcszPath, const char *pcszEAName) { EABINDING eab; eab.bFlags = 0; eab.bNameLength = strlen(pcszEAName); eab.pszName = (PSZ)pcszEAName; eab.usValueLength = 0; eab.pszValue = 0; return (eaPathWriteOne(pcszPath, &eab)); } /* *@@category: Helpers\Control program helpers\Extended attributes\Translation helpers * these functions are built on top of the regular EA helpers * (see eah.c) to convert standard types to EAs. */ /******************************************************************** * * Translation funcs * ********************************************************************/ /* *@@ eaQueryEAType: * this returns the type of the EA stored * in the given EA binding. * See CPREF for the EAT_* codes which are * returned here. * Returns 0 upon errors. * *@@added V0.9.0 [umoeller] */ USHORT eaQueryEAType(PEABINDING peab) { USHORT usReturn = 0; if (peab) if (peab->pszValue) // first USHORT always has EAT_* flag usReturn = *((PUSHORT)(peab->pszValue)); return (usReturn); } /* *@@ eaCreatePSZFromBinding: * this returns a new PSZ for the given EABINDING, * if this is a string EA (EAT_ASCII). * This PSZ should be free()'d after use. * Otherwise, NULL is returned. * *@@added V0.9.0 [umoeller] *@@changed V0.9.2 (2000-03-12) [umoeller]: added checks for NULl strings */ PSZ eaCreatePSZFromBinding(PEABINDING peab) { PSZ pszReturn = NULL; if (peab) { if (eaQueryEAType(peab) == EAT_ASCII) { // OK: next USHORT has length of string PUSHORT pusLength = (PUSHORT)(peab->pszValue + 2); if (*pusLength) { pszReturn = (PSZ)malloc(*pusLength + 1); memcpy(pszReturn, peab->pszValue + 4, *pusLength); // add null terminator *(pszReturn + (*pusLength)) = 0; } } } return (pszReturn); } /* *@@ eaCreateBindingFromPSZ: * reverse to eaCreatePSZFromBinding, this creates * a new EABINDING from the given PSZ, which * can be used with the write-EA functions. * This EA is of type EAT_ASCII and will be * made non-critical (peab->bFlags = 0). * * If the given string is NULL or empty, * a binding is created anyway; however, usValueLength * and pszValue will be set to NULL so that the string * can be deleted. * * Returns NULL only upon malloc() errors. * *@@added V0.9.0 [umoeller] *@@changed V0.9.1 (2000-01-22) [umoeller]: fixed null-string behavior *@@changed V0.9.2 (2000-02-26) [umoeller]: fixed null-string behaviour AGAIN */ PEABINDING eaCreateBindingFromPSZ(const char *pcszEAName, // in: EA name (e.g. ".LONGNAME") const char *pcszInput) // in: string for EAT_ASCII EA { PEABINDING peab = (PEABINDING)malloc(sizeof(EABINDING)); if (peab) { SHORT cbString = 0; if (pcszInput) cbString = strlen(pcszInput); peab->bFlags = 0; peab->bNameLength = strlen(pcszEAName); peab->pszName = strdup(pcszEAName); if (cbString) { // non-null string: peab->usValueLength = cbString + 4; peab->pszValue = (PSZ)malloc(peab->usValueLength); if (peab->pszValue) { // set first USHORT to EAT_ASCII *((PUSHORT)(peab->pszValue)) = EAT_ASCII; // set second USHORT to length of string *((PUSHORT)(peab->pszValue + 2)) = cbString; // copy string to byte 4 (no null-terminator) memcpy(peab->pszValue + 4, pcszInput, cbString); } else { // malloc error: if (peab->pszName) free(peab->pszName); free(peab); peab = NULL; } } else { // null string: peab->usValueLength = 0; peab->pszValue = NULL; } } return (peab); } /* *@@ eaQueryMVCount: * this returns the number of subitems in a * multi-value EA. This works for both EAT_MVMT * and EAT_MVST (multi-type and single-type) EAs. * Returns 0 upon errors. * *@@added V0.9.0 [umoeller] *@@changed V0.9.2 (2000-03-12) [umoeller]: added more error checks */ USHORT eaQueryMVCount(PEABINDING peab, // in: EA binding to examine (must be EAT_MVMT or EAT_MVST) PUSHORT pusCodepage, // out: codepage found in binding (ptr can be NULL) PUSHORT pusEAType) // out: either EAT_MVST or EAT_MVMT (ptr can be NULL) { USHORT usReturn = 0; USHORT usEAType = eaQueryEAType(peab); if (pusEAType) *pusEAType = usEAType; if (usEAType == EAT_MVST) { // multi-value single-type: // pszValue is as follows (all USHORTs) // EAT_MVST usCodepage usCount usDataType data .... // store codepage if (pusCodepage) *pusCodepage = *((PUSHORT)(peab->pszValue + 2)); // find count of entries usReturn = *((PUSHORT)(peab->pszValue + 4)); } // end if (*((PUSHORT)(peab->pszValue)) == EAT_MVST) else if (usEAType == EAT_MVMT) { // multi-value multi-type: // pszValue is as follows (all USHORTs) // EAT_MVMT usCodepage usCount (usDataType data... )... // store codepage if (pusCodepage) *pusCodepage = *((PUSHORT)(peab->pszValue + 2)); // find count of entries usReturn = *((PUSHORT)(peab->pszValue + 4)); } return (usReturn); } /* *@@ eaQueryMVItem: * this returns a pointer to the beginning * of data of a subitem in a multi-value * (EAT_MVST or EAT_MVMT) EA. * * Note that this _only_ works if the data * stored in the multi-value fields has * length-data right after the EAT_* fields. * This is true for EAT_ASCII, for example. * * Example: If the EA value in the * binding is like this: + EAT_MVMT codp count [DataType Data] ... + EAT_MVMT 0000 0002 EAT_ASCII 000A Hello John + EAT_BINARY 0003 0x12 0x21 0x34 * calling this function with usindex==1 would return * a pointer to the 0x12 byte in the EA data, set *pusCodepage * to 0, and set *pusDataLength to 3. * *@@added V0.9.0 [umoeller] *@@changed V0.9.2 (2000-03-12) [umoeller]: added more error checks */ PSZ eaQueryMVItem(PEABINDING peab, // in: binding to examing USHORT usIndex, // in: item to search (starting at 0) PUSHORT pusCodepage, // out: codepage found in binding (ptr can be NULL) PUSHORT pusEAType, // out: EAT_* data type of data that the return value points to (ptr can be NULL) PUSHORT pusDataLength) // out: length of data that the return value points to (ptr can be NULL) { PSZ pszReturn = NULL; USHORT usEAType = eaQueryEAType(peab); if (usEAType == EAT_MVST) { // multi-value single-type: // pszValue is as follows (all USHORTs) // EAT_MVST usCodepage usCount usDataType data .... USHORT usCount, usDataType; PUSHORT pusLengthThis; PSZ pData; // store codepage if (pusCodepage) *pusCodepage = *((PUSHORT)(peab->pszValue + 2)); // find count of entries usCount = *((PUSHORT)(peab->pszValue + 4)); if (usIndex < usCount) { // find data type usDataType = *((PUSHORT)(peab->pszValue + 6)); if ( (usDataType == EAT_ASCII) || (usDataType == EAT_BINARY) ) { USHORT us = 0; // find beginning of data (after length word) pData = peab->pszValue + 10; while (us < usIndex) { pusLengthThis = (PUSHORT)(pData - 2); pData += *pusLengthThis + 2; us++; } // pData now points to the actual data; // pData - 2 has the length of the subvalue // return values if (pusEAType) *pusEAType = usDataType; // same for all MVST subvalues if (pusDataLength) *pusDataLength = *((PUSHORT)(pData - 2)); pszReturn = pData; } } } // end if (usEAType == EAT_MVST) else if (usEAType == EAT_MVMT) { // multi-value multi-type: // pszValue is as follows (all USHORTs) // EAT_MVMT usCodepage usCount (usDataType data... )... USHORT usCount; // usDataType; PUSHORT pusLengthThis; PSZ pData; // store codepage if (pusCodepage) *pusCodepage = *((PUSHORT)(peab->pszValue + 2)); // find count of entries usCount = *((PUSHORT)(peab->pszValue + 4)); if (usIndex < usCount) { USHORT us = 0; pData = peab->pszValue + 6; while (us < usIndex) { PUSHORT pusDataType = (PUSHORT)(pData); if ( (*pusDataType == EAT_ASCII) || (*pusDataType == EAT_BINARY) ) { pusLengthThis = (PUSHORT)(pData + 2); pData += *pusLengthThis + 4; us++; } else { pData = 0; break; } } // pData now points to the usDataType field // of the subvalue; // pData + 2 is the length of the subvalue; // pData + 4 is the actual data // return values if (pData) { if (pusEAType) *pusEAType = *((PUSHORT)(pData)); // different for each MVMT item if (pusDataLength) *pusDataLength = *((PUSHORT)(pData + 2)); pszReturn = pData + 4; } } } // end if (usEAType == EAT_MVMT) return (pszReturn); } /* *@@ eaCreatePSZFromMVBinding: * this returns a new PSZ for the given EABINDING, * if this is a multi-value, multi-type EA (EAT_MVMT), * all of whose subitems are of EAT_ASCII though. * Note that this does _not_ work for EAT_MVST EAs. * * This format is used by the .COMMENTS and .KEYPHRASES * system EAs, for example. * * The different subitems will be separated by what * you specify in pszSeparator. You can, for example, * specify \n\r\0 to have CR-LF separators. * * This PSZ should be free()'d after use. * On errors, NULL is returned. * *@@added V0.9.0 [umoeller] *@@changed V0.9.2 (2000-03-12) [umoeller]: added more error checks */ PSZ eaCreatePSZFromMVBinding(PEABINDING peab, // in: EAT_MVMT binding const char *pcszSeparator, // in: null-terminated string used as separator PUSHORT pusCodepage) // out: codepage found in binding (ptr can be NULL) { PSZ pszTotal = NULL; // this will hold the whole string if (peab) { USHORT usEAType = 0; USHORT usMVCount = eaQueryMVCount(peab, pusCodepage, &usEAType); if ( (usMVCount) // items found? && (usEAType == EAT_MVMT) // rule out EAT_MVST, which we cannot handle here ) { // EAT_MVMT items found: // go thru all of them USHORT us = 0; USHORT cbComment = 0; USHORT cbSeparator = strlen(pcszSeparator); while (us < usMVCount) { USHORT usEATypeThis = 0; USHORT usEALenThis = 0; PSZ pszSubItem = eaQueryMVItem(peab, us, // index NULL, // codepage &usEATypeThis, &usEALenThis); if (!pszSubItem) break; if (usEATypeThis == EAT_ASCII) { if (usEALenThis) { PSZ pszTemp = pszTotal; PSZ pTarget; cbComment += usEALenThis + cbSeparator; pszTotal = (PSZ)malloc(cbComment); if (pszTemp) { // not first loop: append EA value USHORT cbCommentOld = strlen(pszTemp); // copy previous compiled string strcpy(pszTotal, pszTemp); // append separator memcpy(pszTotal + cbCommentOld, pcszSeparator, cbSeparator); // copy the rest after the separator (below) pTarget = pszTotal + cbCommentOld + cbSeparator; free(pszTemp); } else // first loop: copy to beginning pTarget = pszTotal; // copy EA value memcpy(pTarget, pszSubItem, usEALenThis); // append null terminator *(pTarget + usEALenThis) = 0; } } // if (usEATypeThis == EAT_ASCII) us++; } // end while (us < usMVCount) } // end if (usMVCount) } return (pszTotal); } /* *@@ eaCreateMVBindingFromPSZ: * reverse to eaCreatePSZFromMVBinding, this * parses pszInput and creates an EAT_MVMT * EABINDING, all of whose subvalues are of * EAT_ASCII. The EABINDING is non-critical * (peab->bFlags = 0). * * pszSeparator is used in the analysis of pszInput * as the separator string (e.g. "\r\n"). * * Returns NULL if the given string is NULL. * * Example: + PSZ pszInput = "Line 1\r\nLine2\r\nLine3\r\n"; + PEABINDING peab = eaCreateMVBindingFromPSZ(".KEYPHRASES", + pszInput, + "\r\n", + 0); // codepage * will create an EABINDING with the following pszValue: + EAT_MVMT codp usCount (usDataType usLen data...) + EAT_MVMT 0 3 EAT_ASCII 6 Line 1 + EAT_ASCII 5 Line2 + EAT_ASCII 5 Line3 * *@@added V0.9.0 [umoeller] */ PEABINDING eaCreateMVBindingFromPSZ(const char *pcszEAName, // in: EA name (e.g. ".KEYPHRASES") const char *pcszInput, // in: string to parse const char *pcszSeparator, // in: separator used in pszInput USHORT usCodepage) // in: codepage to set in EAT_MVMT { PEABINDING peab = NULL; if (pcszInput) { if ((peab = (PEABINDING)malloc(sizeof(EABINDING)))) { const char *p = pcszInput, *pSource; PSZ pTarget; USHORT cbInput = strlen(pcszInput), cbSep = strlen(pcszSeparator), usSepCount = 0, cbToAlloc = 0, cbThis, us; peab->bFlags = 0; peab->bNameLength = strlen(pcszEAName); peab->pszName = strdup(pcszEAName); // now count the number of pszSeparators in pszInput while ((p = strstr(p, pcszSeparator))) { usSepCount++; p += cbSep; } // usSepCount now has the separator count; that means // we have (usSepCount + 1) data fields // allocate the memory we need for the total EA: // EAT_MVMT usCodepage usCount (usDataType data... )... cbToAlloc = 6 // six fixed bytes for (EAT_MVMT usCodepage usCount) + cbInput - (usSepCount * cbSep) // length of input string minus length of all separators + ((usSepCount + 1) * 4); // for each data field (of which we'll have (usSepCount + 1)), // we need an extra four bytes for EAT_ASCII and the length peab->usValueLength = cbToAlloc; peab->pszValue = (PSZ)malloc(cbToAlloc + 1); // now initialize the first three fields: *((PUSHORT)(peab->pszValue)) = EAT_MVMT; *((PUSHORT)(peab->pszValue + 2)) = usCodepage; *((PUSHORT)(peab->pszValue + 4)) = (usSepCount + 1); // set pointer to first field pSource = pcszInput; pTarget = peab->pszValue + 6; // now go thru all fields except the last for (us = 0; us < usSepCount; // exclude the last field us++) { // find the next separator PSZ pNextSep = strstr(pSource, pcszSeparator); // calculate the length of the substring cbThis = pNextSep - pSource; // set data type in field *((PUSHORT)(pTarget)) = EAT_ASCII; // set length of this field *((PUSHORT)(pTarget + 2)) = cbThis; // copy data memcpy(pTarget + 4, pSource, cbThis); // advance source to the next (after substring and separator) pSource += cbThis + cbSep; // advance target to the next (after substring and (EAT_ASCII usLen) pTarget += cbThis + 4; } // now handle the last field cbThis = strlen(pSource); *((PUSHORT)(pTarget)) = EAT_ASCII; *((PUSHORT)(pTarget + 2)) = cbThis; memcpy(pTarget + 4, pSource, cbThis); } // end if (peab) } // end if (pszInput) return (peab); } /******************************************************************** * * EA helper funcs * ********************************************************************/ /* * These get called by the functions above. * */ /* *@@ ReadEAList: * */ STATIC PEALIST ReadEAList(ULONG type, // in: ENUMEA_REFTYPE_FHANDLE or ENUMEA_REFTYPE_PATH 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 = (PEALIST)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); } /* *@@ ReadEAByIndex: * */ STATIC PEABINDING ReadEAByIndex(ULONG type, // in: ENUMEA_REFTYPE_FHANDLE or ENUMEA_REFTYPE_PATH PVOID pfile, ULONG index) { PDENA2 dena = (ReadDenaByIndex(type, pfile, index)); return ((dena == 0) ? 0 : (GetEAValue(type, pfile, dena))); } /* *@@ ReadEAByName: * */ STATIC PEABINDING ReadEAByName(ULONG type, // in: ENUMEA_REFTYPE_FHANDLE or ENUMEA_REFTYPE_PATH 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; } } /* *@@ ReadDenaByIndex: * fills a DENA2 structure by calling DosEnumAttribute. * * DENA2 is typedef'd to FEA2 in the OS/2 headers. * FEA2 defines the format for setting the full extended * attributes in the file. * + typedef struct _FEA2 { + ULONG oNextEntryOffset; // Offset to next entry. + BYTE fEA; // Extended attributes flag. + BYTE cbName; // Length of szName, not including NULL. + USHORT cbValue; // Value length. + CHAR szName[1]; // Extended attribute name. + } FEA2; + typedef FEA2 *PFEA2; * * Returns 0 on errors. * *@@changed V0.9.2 (2000-03-12) [umoeller]: added more error checking *@@changed V0.9.4 (2000-08-03) [umoeller]: added even more error checking; this fixed problems with swapper.dat and such */ STATIC PDENA2 ReadDenaByIndex(ULONG type, // in: ENUMEA_REFTYPE_FHANDLE or ENUMEA_REFTYPE_PATH PVOID pfile, // in: file handle or name ULONG index) // in: EA index (>= 1) { ULONG count = 1; PDENA2 dena = (PDENA2)malloc(500); // 500 is magic -- IBM doesn't explain. APIRET arc = DosEnumAttribute(type, pfile, index, dena, 500, (&count), ENUMEA_LEVEL_NO_VALUE); if ( (arc != NO_ERROR) || (count == 0) // check error code? ) { free(dena); return (0); } else return (dena); } /* *@@ GetEAValue: * *@@changed V0.9.1 (2000-01-30) [umoeller]: now returning NULL upon errors */ STATIC PEABINDING GetEAValue(ULONG type, // in: ENUMEA_REFTYPE_FHANDLE or ENUMEA_REFTYPE_PATH PVOID pfile, // in: file handle or path PDENA2 dena) // in: buffer allocated by ReadDenaByIndex { ULONG level = FIL_QUERYEASFROMLIST; EAOP2 eaop; ULONG size = (sizeof(eaop)); APIRET arc = NO_ERROR; SetupQueryEAInfo(dena, (&eaop)); // this freeds dena if (type == ENUMEA_REFTYPE_FHANDLE) arc = DosQueryFileInfo((* ((PHFILE) pfile)), level, (&eaop), size); else arc = DosQueryPathInfo((PSZ)pfile, level, (&eaop), size); // changed V0.9.0 (99-11-28) [umoeller]; this now works with C free(eaop.fpGEA2List); if (arc == NO_ERROR) return (ConvertFeal2Binding(eaop.fpFEA2List)); else return NULL; } /* *@@ SetupQueryEAInfo: * prepares a EAOP2 structure for querying EAs. * This also frees dena, since it's no longer used. */ STATIC void SetupQueryEAInfo(PDENA2 dena, // in: buffer allocated by ReadDenaByIndex PEAOP2 eaop) // out: EAOP2 to fill { unsigned int geal_size = ((sizeof (GEA2LIST)) + (dena->cbName)); unsigned int feal_size = ((sizeof (FEA2LIST)) + (dena->cbName) + (dena->cbValue)); (eaop->fpGEA2List) = (GEA2LIST*)(malloc(geal_size)); ((eaop->fpGEA2List)->cbList) = geal_size; (eaop->fpFEA2List) = (FEA2LIST*)(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); } /* *@@ ConvertFeal2Binding: * creates a new EABINDING from the given FEA2LIST * and frees feal. Returns NULL upon errors. * *@@changed V0.9.1 (2000-01-30) [umoeller]: now returning NULL upon errors */ STATIC PEABINDING ConvertFeal2Binding(PFEA2LIST feal) { PFEA2 fea = (&((feal->list) [0])); PEABINDING binding = (PEABINDING)(malloc(sizeof (EABINDING))); if (binding) { (EA_BINDING_FLAGS (binding)) = (fea->fEA); (EA_BINDING_NAME_LENGTH (binding)) = (fea->cbName); (EA_BINDING_VALUE_LENGTH (binding)) = (fea->cbValue); (EA_BINDING_NAME (binding)) = (PSZ)(malloc((fea->cbName) + 1)); strcpy ((EA_BINDING_NAME (binding)), (fea->szName)); (EA_BINDING_VALUE (binding)) = (PSZ)(malloc(fea->cbValue)); memcpy ((EA_BINDING_VALUE (binding)), (&((fea->szName) [(fea->cbName) + 1])), (fea->cbValue)); free(feal); } return (binding); } /* *@@ WriteEAList: * *@@changed V0.9.1 (2000-01-30) [umoeller]: now returning APIRET */ STATIC APIRET WriteEAList(ULONG type, // in: ENUMEA_REFTYPE_FHANDLE or ENUMEA_REFTYPE_PATH PVOID pfile, PEALIST list) { APIRET arc = NO_ERROR; while ( (list != 0) && (arc == NO_ERROR) ) { arc = WriteEA(type, pfile, (EA_LIST_BINDING (list))); list = (EA_LIST_NEXT (list)); } return arc; } /* *@@ WriteEA: * *@@changed V0.9.1 (2000-01-30) [umoeller]: now returning APIRET */ STATIC APIRET WriteEA(ULONG type, // in: ENUMEA_REFTYPE_FHANDLE or ENUMEA_REFTYPE_PATH 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((PSZ)pfile, level, (&eaop), size, DSPI_WRTTHRU); free(eaop.fpFEA2List); return arc; } /* *@@ ConvertBinding2Feal: * */ STATIC PFEA2LIST ConvertBinding2Feal(PEABINDING binding) { unsigned int feal_size = ((sizeof (FEA2LIST)) + (EA_BINDING_NAME_LENGTH (binding)) + (EA_BINDING_VALUE_LENGTH (binding))); PFEA2LIST feal = (PFEA2LIST)(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); } /* ****************************************************************** * * Direct plain-string EA handling * ********************************************************************/ #define USE_EAMVMT 1 /* * eahWriteStringEA: * * (C) Christian Langanke. Moved this here from tmsgfile.c. * *@@added V0.9.3 (2000-05-21) [umoeller] */ APIRET eahWriteStringEA(HFILE hfile, PSZ pszEaName, PSZ pszEaValue) { APIRET rc = NO_ERROR; PFEA2LIST pfea2l = NULL; #ifdef USE_EAMVMT PEAMVMT peamvmt; #else PEASVST peasvst; #endif ULONG ulEAListLen; ULONG ulValueLen; EAOP2 eaop2; // PSZ pszValue; do { // check parameters if ( (hfile == NULLHANDLE) || (pszEaName == NULL) || (*pszEaName == 0) || (pszEaValue == NULL) ) { rc = ERROR_INVALID_PARAMETER; break; } // write EAs ulValueLen = strlen(pszEaValue) + #ifdef USE_EAMVMT sizeof(EAMVMT); #else sizeof(EASVST); #endif ulEAListLen = strlen(pszEaName) + sizeof(FEA2LIST) + ulValueLen; // get memory for FEA2LIST pfea2l = (FEA2LIST*)malloc(ulEAListLen); if (pfea2l == 0) { rc = ERROR_NOT_ENOUGH_MEMORY; break; } // init FEA2LIST eaop2.fpGEA2List = NULL; eaop2.fpFEA2List = pfea2l; memset(pfea2l, 0, ulEAListLen); // write timeframe EA pfea2l->cbList = ulEAListLen; pfea2l->list[0].cbName = strlen(pszEaName); strcpy(pfea2l->list[0].szName, pszEaName); // delete attribute if value empty if (strlen(pszEaValue) == 0) pfea2l->list[0].cbValue = 0; else { pfea2l->list[0].cbValue = ulValueLen; #ifdef USE_EAMVMT // multi value multi type peamvmt = (PEAMVMT) NEXTSTR(pfea2l->list[0].szName); peamvmt->usType = EAT_MVMT; peamvmt->usCodepage = 0; peamvmt->usEntries = 1; peamvmt->usEntryType = EAT_ASCII; peamvmt->usEntryLen = strlen(pszEaValue); memcpy(&peamvmt->chEntry[0], pszEaValue, peamvmt->usEntryLen); #else // single value single type peasvst = NEXTSTR(pfea2l->list[0].szName); peasvst->usType = EAT_ASCII; peasvst->usEntryLen = strlen(pszEaValue); memcpy(&peasvst->chEntry[0], pszEaValue, peasvst->usEntryLen); #endif } // set the new EA value rc = DosSetFileInfo(hfile, FIL_QUERYEASIZE, &eaop2, sizeof(eaop2)); } while (FALSE); // cleanup if (pfea2l) free(pfea2l); return rc; } /* * eahReadStringEA: * * (C) Christian Langanke. Moved this here from tmsgfile.c. * *@@added V0.9.3 (2000-05-21) [umoeller] */ APIRET eahReadStringEA(PSZ pszFileName, PSZ pszEaName, PSZ pszBuffer, PULONG pulBuflen) { APIRET rc = NO_ERROR; FILESTATUS4 fs4; EAOP2 eaop2; PGEA2LIST pgea2l = NULL; PFEA2LIST pfea2l = NULL; PGEA2 pgea2; PFEA2 pfea2; ULONG ulGea2Len = 0; ULONG ulFea2Len = 0; PEASVST peasvst; PEAMVMT peamvmt; ULONG ulRequiredLen; do { // check parameters if ((pszFileName == NULL) || (pszEaName == NULL) || (*pszEaName == 0) || (pulBuflen == NULL)) { rc = ERROR_INVALID_PARAMETER; break; } // initialize target buffer if (pszBuffer) memset(pszBuffer, 0, *pulBuflen); // get EA size rc = DosQueryPathInfo(pszFileName, FIL_QUERYEASIZE, &fs4, sizeof(fs4)); if (rc != NO_ERROR) break; // no eas here ? if (fs4.cbList == 0) { pulBuflen = 0; break; } // determine required space // - for ulFea2Len use at least 2 * Gea2Len because // buffer needs at least to be Geal2Len even for an empty // attribute, otherwise rc == ERROR_BUFFER_OVERFLOW ! ulGea2Len = sizeof(GEA2LIST) + strlen(pszEaName); ulFea2Len = 2 * MAX(fs4.cbList, ulGea2Len); // get memory for GEA2LIST if ((pgea2l = (GEA2LIST*)malloc(ulGea2Len)) == 0) { rc = ERROR_NOT_ENOUGH_MEMORY; break; } memset(pgea2l, 0, ulGea2Len); // get memory for FEA2LIST if ((pfea2l = (FEA2LIST*)malloc(ulFea2Len)) == 0) { rc = ERROR_NOT_ENOUGH_MEMORY; break; } memset(pfea2l, 0, ulFea2Len); // init ptrs and do the query memset(&eaop2, 0, sizeof(EAOP2)); eaop2.fpGEA2List = pgea2l; eaop2.fpFEA2List = pfea2l; pfea2l->cbList = ulFea2Len; pgea2l->cbList = ulGea2Len; pgea2 = &pgea2l->list[0]; pfea2 = &pfea2l->list[0]; pgea2->oNextEntryOffset = 0; pgea2->cbName = strlen(pszEaName); strcpy(pgea2->szName, pszEaName); rc = DosQueryPathInfo(pszFileName, FIL_QUERYEASFROMLIST, &eaop2, sizeof(eaop2)); if (rc != NO_ERROR) break; // check first entry only peamvmt = (PEAMVMT) ((PBYTE) pfea2->szName + pfea2->cbName + 1); // is it MVMT ? then adress single EA ! if (peamvmt->usType == EAT_MVMT) { peasvst = (PEASVST) & peamvmt->usEntryType; } else peasvst = (PEASVST) peamvmt; // is entry empty ? if (peasvst->usEntryLen == 0) { rc = ERROR_INVALID_EA_NAME; break; } // is it ASCII ? if (peasvst->usType != EAT_ASCII) { rc = ERROR_INVALID_DATA; break; } // check buffer and hand over value ulRequiredLen = peasvst->usEntryLen + 1; if (*pulBuflen < ulRequiredLen) { *pulBuflen = ulRequiredLen; rc = ERROR_BUFFER_OVERFLOW; break; } // hand over len *pulBuflen = ulRequiredLen; // hand over value if (pszBuffer) memcpy(pszBuffer, peasvst->chEntry, peasvst->usEntryLen); } while (FALSE); // cleanup if (pgea2l) free(pgea2l); if (pfea2l) free(pfea2l); return rc; }