home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / xwphescr.zip / XWPH0208.ZIP / src / helpers / eah.c < prev    next >
C/C++ Source or Header  |  2002-08-08  |  48KB  |  1,501 lines

  1.  
  2. /*
  3.  *@@sourcefile eah.c:
  4.  *      contains helper functions for handling Extended Attributes.
  5.  *      See explanations below.
  6.  *
  7.  *      Function prefixes (new with V0.81):
  8.  *      --  ea*         EA helper functions
  9.  *
  10.  *      Usage: All OS/2 programs.
  11.  *
  12.  *      This file is new with V0.81 and contains all the EA functions
  13.  *      that were in helpers.c previously.
  14.  *
  15.  *      Most of the functions in here come in pairs: one for working
  16.  *      on EAs based on a path (eaPath*), one for open HFILEs (eaHFile*).
  17.  *      Most functions return structures called EABINDING which should
  18.  *      be freed again later.
  19.  *
  20.  *      <B>Example:</B>
  21.  +          PEABINDING peab = eaPathReadOneByName("C:\Desktop", ".LONGNAME");
  22.  +          if (peab)
  23.  +              PSZ pszLongName = peab->pszValue;
  24.  +          eaFreeBinding(peab);
  25.  *
  26.  *      Note: Version numbering in this file relates to XWorkplace version
  27.  *            numbering.
  28.  *
  29.  *@@header "helpers\eah.h"
  30.  */
  31.  
  32. /*
  33.  *      Most of the code in this file dealing with Extended Attributes is based
  34.  *      on code (w) by Chris Hanson (cph@zurich.ai.mit.edu).
  35.  *      Copyright (c) 1995 Massachusetts Institute of Technology.
  36.  *
  37.  *      The original code is available as EALIB.ZIP at Hobbes.
  38.  *
  39.  *      This material was developed by the Scheme project at the Massachusetts
  40.  *      Institute of Technology, Department of Electrical Engineering and
  41.  *      Computer Science.  Permission to copy this software, to redistribute
  42.  *      it, and to use it for any purpose is granted, subject to the following
  43.  *      restrictions and understandings.
  44.  *
  45.  *      1. Any copy made of this software must include this copyright notice
  46.  *      in full.
  47.  *
  48.  *      2. Users of this software agree to make their best efforts (a) to
  49.  *      return to the MIT Scheme project any improvements or extensions that
  50.  *      they make, so that these may be included in future releases; and (b)
  51.  *      to inform MIT of noteworthy uses of this software.
  52.  *
  53.  *      3. All materials developed as a consequence of the use of this
  54.  *      software shall duly acknowledge such use, in accordance with the usual
  55.  *      standards of acknowledging credit in academic research.
  56.  *
  57.  *      4. MIT has made no warrantee or representation that the operation of
  58.  *      this software will be error-free, and MIT is under no obligation to
  59.  *      provide any services, by way of maintenance, update, or otherwise.
  60.  *
  61.  *      5. In conjunction with products arising from the use of this material,
  62.  *      there shall be no use of the name of the Massachusetts Institute of
  63.  *      Technology nor of any adaptation thereof in any advertising,
  64.  *      promotional, or sales literature without prior written consent from
  65.  *      MIT in each case.
  66.  *
  67.  *      Copyright (C) 1995 Massachusetts Institute of Technology.
  68.  *      Copyright (C) 1997-2002 Ulrich Möller.
  69.  *      This file is part of the "XWorkplace helpers" source package.
  70.  *      This is free software; you can redistribute it and/or modify
  71.  *      it under the terms of the GNU General Public License as published
  72.  *      by the Free Software Foundation, in version 2 as it comes in the
  73.  *      "COPYING" file of the XWorkplace main distribution.
  74.  *      This program is distributed in the hope that it will be useful,
  75.  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
  76.  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  77.  *      GNU General Public License for more details.
  78.  */
  79.  
  80. #define OS2EMX_PLAIN_CHAR
  81.     // this is needed for "os2emx.h"; if this is defined,
  82.     // emx will define PSZ as _signed_ char, otherwise
  83.     // as unsigned char
  84.  
  85. #define INCL_DOSFILEMGR
  86. #define INCL_DOSERRORS
  87. #include <os2.h>
  88.  
  89. #include <stdlib.h>
  90. #include <string.h>
  91.  
  92. #include "setup.h"                      // code generation and debugging options
  93.  
  94. #include "helpers\eah.h"
  95.  
  96. #pragma hdrstop
  97.  
  98. /*
  99.  *@@category: Helpers\Control program helpers\Extended attributes
  100.  *      See eah.c.
  101.  */
  102.  
  103. /********************************************************************
  104.  *
  105.  *   Extended Attribute handling
  106.  *
  107.  ********************************************************************/
  108.  
  109. #define EA_BINDING_FLAGS(binding) ((binding)->bFlags)
  110. #define EA_BINDING_NAME_LENGTH(binding) ((binding)->bNameLength)
  111. #define EA_BINDING_VALUE_LENGTH(binding) ((binding)->usValueLength)
  112. #define EA_BINDING_NAME(binding) ((binding)->pszName)
  113. #define EA_BINDING_VALUE(binding) ((binding)->pszValue)
  114.  
  115. #define EA_LIST_BINDING(list) ((list)->peab)
  116. #define EA_LIST_NEXT(list) ((list)->next)
  117.  
  118. // forward declarations to helper funcs at bottom
  119. static PEALIST      ReadEAList(ULONG, PVOID);
  120. static EABINDING *  ReadEAByIndex(ULONG, PVOID, ULONG);
  121. static EABINDING *  ReadEAByName(ULONG, PVOID, PSZ);
  122. static PDENA2       ReadDenaByIndex(ULONG, PVOID, ULONG);
  123. static PEABINDING   GetEAValue(ULONG, PVOID, PDENA2);
  124. static void         SetupQueryEAInfo(PDENA2, PEAOP2);
  125. static PEABINDING   ConvertFeal2Binding(PFEA2LIST);
  126. static APIRET       WriteEAList(ULONG, PVOID, PEALIST);
  127. static APIRET       WriteEA(ULONG, PVOID, PEABINDING);
  128. static PFEA2LIST    ConvertBinding2Feal(PEABINDING);
  129.  
  130. /*
  131.  *@@ eaFreeBinding:
  132.  *      deallocate EA binding memory that was generated
  133.  *      by the ea...Read... procedures below. These procs
  134.  *      assume that "malloc" was used for allocation and
  135.  *      that the "pszName" and "pszValue" fields of each binding
  136.  *      were also allocated using "malloc". "pszValue" may also
  137.  *      be NULL.
  138.  *
  139.  *@@changed V0.9.0 [umoeller]: added check for (binding != NULL)
  140.  */
  141.  
  142. void eaFreeBinding(PEABINDING peab)
  143. {
  144.     if (peab)
  145.     {
  146.         free(EA_BINDING_NAME(peab));
  147.         if ((EA_BINDING_VALUE(peab)) != 0)
  148.             free(EA_BINDING_VALUE(peab));
  149.         free(peab);
  150.     }
  151. }
  152.  
  153. /*
  154.  *@@ eaFreeList:
  155.  *      like eaFreeBinding, but for an EA binding list.
  156.  *      This calls eaFreeBinding for each list item.
  157.  *
  158.  *@@changed V0.9.0 [umoeller]: added check for (list != NULL)
  159.  */
  160.  
  161. void eaFreeList(PEALIST list)
  162. {
  163.     if (list)
  164.     {
  165.         while (list != 0)
  166.         {
  167.             PEALIST next = (EA_LIST_NEXT (list));
  168.             eaFreeBinding(EA_LIST_BINDING (list));
  169.             free(list);
  170.             list = next;
  171.         }
  172.     }
  173. }
  174.  
  175. /* ******************************************************************
  176.  *
  177.  *   Read-EA functions
  178.  *
  179.  ********************************************************************/
  180.  
  181. /*
  182.  * All of the following functions come in two flavors:
  183.  *
  184.  *      eaHFile*    operate on an open file handle.
  185.  *
  186.  *      eaPath*     operate on any file specified by its
  187.  *                  filename, which may be fully qualified.
  188.  */
  189.  
  190. /*
  191.  *@@ eaPathQueryTotalSize:
  192.  *      returns the total size of all EAs for a given file.
  193.  *      This does not use the other EA functions, but
  194.  *      accesses the EAs directly, so this is a lot quicker
  195.  *      if you only want the total EA size without accessing
  196.  *      the EA data.
  197.  */
  198.  
  199. ULONG eaPathQueryTotalSize(const char *pcszPath)
  200. {
  201.     APIRET  arc;
  202.     ULONG   ulTotalEASize = 0;
  203.     FILEFINDBUF4   ffb4;
  204.  
  205.     if (!(arc = DosQueryPathInfo((PSZ)pcszPath,
  206.                                  FIL_QUERYEASIZE,
  207.                                  &ffb4,
  208.                                  sizeof(FILEFINDBUF4))))
  209.     {
  210.         BYTE abBuf[2000];
  211.         LONG lCount = 0;
  212.         PDENA2 pdena2;
  213.  
  214.         lCount = -1;
  215.  
  216.         if (!(arc = DosEnumAttribute(ENUMEA_REFTYPE_PATH,
  217.                                      (PSZ)pcszPath,
  218.                                      1,
  219.                                      abBuf,
  220.                                      sizeof(abBuf),
  221.                                      (PULONG)&lCount,
  222.                                      ENUMEA_LEVEL_NO_VALUE)))
  223.         {
  224.             // ulCount now contains the EA count
  225.  
  226.             pdena2 = (PDENA2)abBuf;
  227.  
  228.             if (lCount > 0)
  229.             {
  230.                 ulTotalEASize = pdena2->cbName + 8;
  231.  
  232.                 while (lCount > 0)
  233.                 {
  234.                     ulTotalEASize += (pdena2->cbValue + sizeof(DENA2));
  235.                     lCount--;
  236.                     pdena2 = (PDENA2) (((PBYTE) pdena2) +
  237.                                   pdena2->oNextEntryOffset);
  238.  
  239.                 }
  240.             }
  241.         }
  242.     }
  243.  
  244.     return (ulTotalEASize);
  245. }
  246.  
  247. /*
  248.  *@@ eaPathReadAll:
  249.  *      reads all of the extended attributes into an EALIST.
  250.  *      Returns NULL if no EAs were found.
  251.  *      The returned list should be freed using eaFreeList.
  252.  */
  253.  
  254. PEALIST eaPathReadAll(const char *pcszPath)
  255. {
  256.     return (ReadEAList(ENUMEA_REFTYPE_PATH, (PSZ)pcszPath));
  257. }
  258.  
  259. /*
  260.  *@@ eaHFileReadAll:
  261.  *      like eaPathReadAll, but for an open file handle.
  262.  */
  263.  
  264. PEALIST eaHFileReadAll(HFILE hfile)
  265. {
  266.     return (ReadEAList(ENUMEA_REFTYPE_FHANDLE, (&hfile)));
  267. }
  268.  
  269. /*
  270.  *@@ eaPathReadOneByIndex:
  271.  *      returns one EA specified by a given index, counting
  272.  *      from 1. Returns NULL if the specified index was not
  273.  *      found, either because the file has no EAs at all or
  274.  *      the index is too large.
  275.  *      The returned binding should be freed using eaFreeBinding.
  276.  */
  277.  
  278. PEABINDING eaPathReadOneByIndex(const char *pcszPath, ULONG index)
  279. {
  280.     return (ReadEAByIndex(ENUMEA_REFTYPE_PATH, (PSZ)pcszPath, index));
  281. }
  282.  
  283. /*
  284.  *@@ eaHFileReadOneByIndex:
  285.  *      like eaPathReadOneByIndex, but for an open file handle.
  286.  */
  287.  
  288. PEABINDING eaHFileReadOneByIndex(HFILE hfile, ULONG index)
  289. {
  290.     return (ReadEAByIndex(ENUMEA_REFTYPE_FHANDLE, (&hfile), index));
  291. }
  292.  
  293. /*
  294.  *@@ eaPathReadOneByName:
  295.  *      returns one EA specified by the given EA name (e.g.
  296.  *      ".LONGNAME"). Returns NULL if not found.
  297.  *      The returned binding should be freed using eaFreeBinding.
  298.  */
  299.  
  300. PEABINDING eaPathReadOneByName(const char *pcszPath, const char *pcszEAName)
  301. {
  302.     return (ReadEAByName(ENUMEA_REFTYPE_PATH, (PSZ)pcszPath, (PSZ)pcszEAName));
  303. }
  304.  
  305. /*
  306.  *@@ eaHFileReadOneByName:
  307.  *      like eaPathReadOneByName, but for an open file handle.
  308.  */
  309.  
  310. PEABINDING eaHFileReadOneByName(HFILE hfile, const char *pcszEAName)
  311. {
  312.     return (ReadEAByName(ENUMEA_REFTYPE_FHANDLE, (&hfile), (PSZ)pcszEAName));
  313. }
  314.  
  315. /* ******************************************************************
  316.  *
  317.  *   Write-EA functions
  318.  *
  319.  ********************************************************************/
  320.  
  321. /*
  322.  *@@ eaPathWriteAll:
  323.  *      writes a list of EAs to a given file. These EAs
  324.  *      are added to possibly existing EAs on the file;
  325.  *      existing EAs will be overwritten.
  326.  *
  327.  *      A given EA is deleted if its EABINDING.usValueLength
  328.  *      field is 0; only in that case, the EABINDING.value
  329.  *      field may also be NULL.
  330.  *
  331.  *@@changed V0.9.7 (2000-11-30) [umoeller]: now returning APIRET
  332.  */
  333.  
  334. APIRET eaPathWriteAll(const char *pcszPath, PEALIST list)
  335. {
  336.     return (WriteEAList(ENUMEA_REFTYPE_PATH, (PSZ)pcszPath, list));
  337. }
  338.  
  339. /*
  340.  *@@ eaHFileWriteAll:
  341.  *      like eaPathWriteAll, but for an open file handle.
  342.  *
  343.  *@@changed V0.9.7 (2000-11-30) [umoeller]: now returning APIRET
  344.  */
  345.  
  346. APIRET eaHFileWriteAll(HFILE hfile, PEALIST list)
  347. {
  348.     return (WriteEAList(ENUMEA_REFTYPE_FHANDLE, (&hfile), list));
  349. }
  350.  
  351. /*
  352.  *@@ eaPathWriteOne:
  353.  *      adds one EA to a given file. If the EA exists
  354.  *      alredy, it is overwritten.
  355.  *
  356.  *      A given EA is deleted if its EABINDING.usValueLength
  357.  *      field is 0; only in that case, the EABINDING.pszValue
  358.  *      field may also be NULL.
  359.  *
  360.  *      To delete an EA, you may also use eaPathDeleteOne.
  361.  *
  362.  *@@changed V0.9.7 (2000-11-30) [umoeller]: now returning APIRET
  363.  */
  364.  
  365. APIRET eaPathWriteOne(const char *pcszPath, PEABINDING peab)
  366. {
  367.     return (WriteEA(ENUMEA_REFTYPE_PATH, (PSZ)pcszPath, peab));
  368. }
  369.  
  370. /*
  371.  *@@ eaHFileWriteOne:
  372.  *      like eaPathWriteOne, but for an open file handle.
  373.  *
  374.  *@@changed V0.9.7 (2000-11-30) [umoeller]: now returning APIRET
  375.  */
  376.  
  377. APIRET eaHFileWriteOne(HFILE hfile, PEABINDING peab)
  378. {
  379.     return (WriteEA(ENUMEA_REFTYPE_FHANDLE, (&hfile), peab));
  380. }
  381.  
  382. /*
  383.  *@@ eaPathDeleteOne:
  384.  *      this deletes one EA by constructing a temporary
  385.  *      empty EABINDING for pszEAName and calling
  386.  *      eaPathWriteOne.
  387.  *
  388.  *@@added V0.9.0 [umoeller]
  389.  *@@changed V0.9.7 (2000-11-30) [umoeller]: now returning APIRET
  390.  */
  391.  
  392. APIRET eaPathDeleteOne(const char *pcszPath, const char *pcszEAName)
  393. {
  394.     EABINDING eab;
  395.     eab.bFlags = 0;
  396.     eab.bNameLength = strlen(pcszEAName);
  397.     eab.pszName = (PSZ)pcszEAName;
  398.     eab.usValueLength = 0;
  399.     eab.pszValue = 0;
  400.     return (eaPathWriteOne(pcszPath, &eab));
  401. }
  402.  
  403. /*
  404.  *@@category: Helpers\Control program helpers\Extended attributes\Translation helpers
  405.  *      these functions are built on top of the regular EA helpers
  406.  *      (see eah.c) to convert standard types to EAs.
  407.  */
  408.  
  409. /********************************************************************
  410.  *
  411.  *   Translation funcs
  412.  *
  413.  ********************************************************************/
  414.  
  415. /*
  416.  *@@ eaQueryEAType:
  417.  *      this returns the type of the EA stored
  418.  *      in the given EA binding.
  419.  *      See CPREF for the EAT_* codes which are
  420.  *      returned here.
  421.  *      Returns 0 upon errors.
  422.  *
  423.  *@@added V0.9.0 [umoeller]
  424.  */
  425.  
  426. USHORT eaQueryEAType(PEABINDING peab)
  427. {
  428.     USHORT usReturn = 0;
  429.     if (peab)
  430.         if (peab->pszValue)
  431.             // first USHORT always has EAT_* flag
  432.             usReturn = *((PUSHORT)(peab->pszValue));
  433.     return (usReturn);
  434. }
  435.  
  436. /*
  437.  *@@ eaCreatePSZFromBinding:
  438.  *      this returns a new PSZ for the given EABINDING,
  439.  *      if this is a string EA (EAT_ASCII).
  440.  *      This PSZ should be free()'d after use.
  441.  *      Otherwise, NULL is returned.
  442.  *
  443.  *@@added V0.9.0 [umoeller]
  444.  *@@changed V0.9.2 (2000-03-12) [umoeller]: added checks for NULl strings
  445.  */
  446.  
  447. PSZ eaCreatePSZFromBinding(PEABINDING peab)
  448. {
  449.     PSZ pszReturn = NULL;
  450.  
  451.     if (peab)
  452.     {
  453.         if (eaQueryEAType(peab) == EAT_ASCII)
  454.         {
  455.             // OK: next USHORT has length of string
  456.             PUSHORT pusLength = (PUSHORT)(peab->pszValue + 2);
  457.             if (*pusLength)
  458.             {
  459.                 pszReturn = (PSZ)malloc(*pusLength + 1);
  460.                 memcpy(pszReturn, peab->pszValue + 4, *pusLength);
  461.                 // add null terminator
  462.                 *(pszReturn + (*pusLength)) = 0;
  463.             }
  464.         }
  465.     }
  466.  
  467.     return (pszReturn);
  468. }
  469.  
  470. /*
  471.  *@@ eaCreateBindingFromPSZ:
  472.  *      reverse to eaCreatePSZFromBinding, this creates
  473.  *      a new EABINDING from the given PSZ, which
  474.  *      can be used with the write-EA functions.
  475.  *      This EA is of type EAT_ASCII and will be
  476.  *      made non-critical (peab->bFlags = 0).
  477.  *
  478.  *      If the given string is NULL or empty,
  479.  *      a binding is created anyway; however, usValueLength
  480.  *      and pszValue will be set to NULL so that the string
  481.  *      can be deleted.
  482.  *
  483.  *      Returns NULL only upon malloc() errors.
  484.  *
  485.  *@@added V0.9.0 [umoeller]
  486.  *@@changed V0.9.1 (2000-01-22) [umoeller]: fixed null-string behavior
  487.  *@@changed V0.9.2 (2000-02-26) [umoeller]: fixed null-string behaviour AGAIN
  488.  */
  489.  
  490. PEABINDING eaCreateBindingFromPSZ(const char *pcszEAName, // in: EA name (e.g. ".LONGNAME")
  491.                                   const char *pcszInput)  // in: string for EAT_ASCII EA
  492. {
  493.     PEABINDING peab = (PEABINDING)malloc(sizeof(EABINDING));
  494.     if (peab)
  495.     {
  496.         SHORT cbString = 0;
  497.         if (pcszInput)
  498.             cbString = strlen(pcszInput);
  499.  
  500.         peab->bFlags = 0;
  501.         peab->bNameLength = strlen(pcszEAName);
  502.         peab->pszName = strdup(pcszEAName);
  503.  
  504.         if (cbString)
  505.         {
  506.             // non-null string:
  507.             peab->usValueLength = cbString + 4;
  508.             peab->pszValue = (PSZ)malloc(peab->usValueLength);
  509.             if (peab->pszValue)
  510.             {
  511.                 // set first USHORT to EAT_ASCII
  512.                 *((PUSHORT)(peab->pszValue)) = EAT_ASCII;
  513.                 // set second USHORT to length of string
  514.                 *((PUSHORT)(peab->pszValue + 2)) = cbString;
  515.                 // copy string to byte 4 (no null-terminator)
  516.                 memcpy(peab->pszValue + 4, pcszInput, cbString);
  517.             }
  518.             else
  519.             {
  520.                 // malloc error:
  521.                 if (peab->pszName)
  522.                     free(peab->pszName);
  523.                 free(peab);
  524.                 peab = NULL;
  525.             }
  526.         }
  527.         else
  528.         {
  529.             // null string:
  530.             peab->usValueLength = 0;
  531.             peab->pszValue = NULL;
  532.         }
  533.     }
  534.  
  535.     return (peab);
  536. }
  537.  
  538. /*
  539.  *@@ eaQueryMVCount:
  540.  *      this returns the number of subitems in a
  541.  *      multi-value EA. This works for both EAT_MVMT
  542.  *      and EAT_MVST (multi-type and single-type) EAs.
  543.  *      Returns 0 upon errors.
  544.  *
  545.  *@@added V0.9.0 [umoeller]
  546.  *@@changed V0.9.2 (2000-03-12) [umoeller]: added more error checks
  547.  */
  548.  
  549. USHORT eaQueryMVCount(PEABINDING peab,      // in: EA binding to examine (must be EAT_MVMT or EAT_MVST)
  550.                       PUSHORT pusCodepage,  // out: codepage found in binding (ptr can be NULL)
  551.                       PUSHORT pusEAType)    // out: either EAT_MVST or EAT_MVMT (ptr can be NULL)
  552. {
  553.     USHORT usReturn = 0;
  554.  
  555.     USHORT usEAType = eaQueryEAType(peab);
  556.  
  557.     if (pusEAType)
  558.         *pusEAType = usEAType;
  559.  
  560.     if (usEAType == EAT_MVST)
  561.     {
  562.         // multi-value single-type:
  563.         // pszValue is as follows (all USHORTs)
  564.         // EAT_MVST usCodepage usCount usDataType data ....
  565.  
  566.         // store codepage
  567.         if (pusCodepage)
  568.             *pusCodepage = *((PUSHORT)(peab->pszValue + 2));
  569.         // find count of entries
  570.         usReturn = *((PUSHORT)(peab->pszValue + 4));
  571.     } // end if (*((PUSHORT)(peab->pszValue)) == EAT_MVST)
  572.     else if (usEAType == EAT_MVMT)
  573.     {
  574.         // multi-value multi-type:
  575.         // pszValue is as follows (all USHORTs)
  576.         // EAT_MVMT usCodepage usCount (usDataType data... )...
  577.  
  578.         // store codepage
  579.         if (pusCodepage)
  580.             *pusCodepage = *((PUSHORT)(peab->pszValue + 2));
  581.         // find count of entries
  582.         usReturn = *((PUSHORT)(peab->pszValue + 4));
  583.     }
  584.  
  585.     return (usReturn);
  586. }
  587.  
  588. /*
  589.  *@@ eaQueryMVItem:
  590.  *      this returns a pointer to the beginning
  591.  *      of data of a subitem in a multi-value
  592.  *      (EAT_MVST or EAT_MVMT) EA.
  593.  *
  594.  *      Note that this _only_ works if the data
  595.  *      stored in the multi-value fields has
  596.  *      length-data right after the EAT_* fields.
  597.  *      This is true for EAT_ASCII, for example.
  598.  *
  599.  *      <B>Example:</B> If the EA value in the
  600.  *      binding is like this:
  601.  +          EAT_MVMT  codp  count [DataType   Data] ...
  602.  +          EAT_MVMT  0000  0002  EAT_ASCII   000A Hello John
  603.  +                                EAT_BINARY  0003 0x12 0x21 0x34
  604.  *      calling this function with usindex==1 would return
  605.  *      a pointer to the 0x12 byte in the EA data, set *pusCodepage
  606.  *      to 0, and set *pusDataLength to 3.
  607.  *
  608.  *@@added V0.9.0 [umoeller]
  609.  *@@changed V0.9.2 (2000-03-12) [umoeller]: added more error checks
  610.  */
  611.  
  612. PSZ eaQueryMVItem(PEABINDING peab,            // in: binding to examing
  613.                   USHORT usIndex,             // in: item to search (starting at 0)
  614.                   PUSHORT pusCodepage,        // out: codepage found in binding (ptr can be NULL)
  615.                   PUSHORT pusEAType,          // out: EAT_* data type of data that the return value points to (ptr can be NULL)
  616.                   PUSHORT pusDataLength)      // out: length of data that the return value points to (ptr can be NULL)
  617. {
  618.     PSZ pszReturn = NULL;
  619.  
  620.     USHORT usEAType = eaQueryEAType(peab);
  621.  
  622.     if (usEAType == EAT_MVST)
  623.     {
  624.         // multi-value single-type:
  625.         // pszValue is as follows (all USHORTs)
  626.         // EAT_MVST usCodepage usCount usDataType data ....
  627.         USHORT usCount,
  628.                usDataType;
  629.         PUSHORT pusLengthThis;
  630.         PSZ    pData;
  631.         // store codepage
  632.         if (pusCodepage)
  633.             *pusCodepage = *((PUSHORT)(peab->pszValue + 2));
  634.         // find count of entries
  635.         usCount = *((PUSHORT)(peab->pszValue + 4));
  636.  
  637.         if (usIndex < usCount)
  638.         {
  639.             // find data type
  640.             usDataType = *((PUSHORT)(peab->pszValue + 6));
  641.             if (    (usDataType == EAT_ASCII)
  642.                  || (usDataType == EAT_BINARY)
  643.                )
  644.             {
  645.                 USHORT us = 0;
  646.                 // find beginning of data (after length word)
  647.                 pData = peab->pszValue + 10;
  648.  
  649.                 while (us < usIndex)
  650.                 {
  651.                     pusLengthThis = (PUSHORT)(pData - 2);
  652.                     pData += *pusLengthThis + 2;
  653.                     us++;
  654.                 }
  655.  
  656.                 // pData now points to the actual data;
  657.                 // pData - 2 has the length of the subvalue
  658.  
  659.                 // return values
  660.                 if (pusEAType)
  661.                     *pusEAType = usDataType; // same for all MVST subvalues
  662.                 if (pusDataLength)
  663.                     *pusDataLength = *((PUSHORT)(pData - 2));
  664.                 pszReturn = pData;
  665.             }
  666.         }
  667.     } // end if (usEAType == EAT_MVST)
  668.     else if (usEAType == EAT_MVMT)
  669.     {
  670.         // multi-value multi-type:
  671.         // pszValue is as follows (all USHORTs)
  672.         // EAT_MVMT usCodepage usCount (usDataType data... )...
  673.         USHORT usCount;
  674.                // usDataType;
  675.         PUSHORT pusLengthThis;
  676.         PSZ    pData;
  677.         // store codepage
  678.         if (pusCodepage)
  679.             *pusCodepage = *((PUSHORT)(peab->pszValue + 2));
  680.         // find count of entries
  681.         usCount = *((PUSHORT)(peab->pszValue + 4));
  682.  
  683.         if (usIndex < usCount)
  684.         {
  685.             USHORT us = 0;
  686.             pData = peab->pszValue + 6;
  687.  
  688.             while (us < usIndex)
  689.             {
  690.                 PUSHORT pusDataType = (PUSHORT)(pData);
  691.  
  692.                 if (    (*pusDataType == EAT_ASCII)
  693.                      || (*pusDataType == EAT_BINARY)
  694.                    )
  695.                 {
  696.                     pusLengthThis = (PUSHORT)(pData + 2);
  697.                     pData += *pusLengthThis + 4;
  698.                     us++;
  699.                 }
  700.                 else
  701.                 {
  702.                     pData = 0;
  703.                     break;
  704.                 }
  705.             }
  706.  
  707.             // pData now points to the usDataType field
  708.             // of the subvalue;
  709.             // pData + 2 is the length of the subvalue;
  710.             // pData + 4 is the actual data
  711.  
  712.             // return values
  713.             if (pData)
  714.             {
  715.                 if (pusEAType)
  716.                     *pusEAType = *((PUSHORT)(pData));  // different for each MVMT item
  717.                 if (pusDataLength)
  718.                     *pusDataLength = *((PUSHORT)(pData + 2));
  719.                 pszReturn = pData + 4;
  720.             }
  721.         }
  722.     } // end if (usEAType == EAT_MVMT)
  723.  
  724.     return (pszReturn);
  725. }
  726.  
  727. /*
  728.  *@@ eaCreatePSZFromMVBinding:
  729.  *      this returns a new PSZ for the given EABINDING,
  730.  *      if this is a multi-value, multi-type EA (EAT_MVMT),
  731.  *      all of whose subitems are of EAT_ASCII though.
  732.  *      Note that this does _not_ work for EAT_MVST EAs.
  733.  *
  734.  *      This format is used by the .COMMENTS and .KEYPHRASES
  735.  *      system EAs, for example.
  736.  *
  737.  *      The different subitems will be separated by what
  738.  *      you specify in pszSeparator. You can, for example,
  739.  *      specify \n\r\0 to have CR-LF separators.
  740.  *
  741.  *      This PSZ should be free()'d after use.
  742.  *      On errors, NULL is returned.
  743.  *
  744.  *@@added V0.9.0 [umoeller]
  745.  *@@changed V0.9.2 (2000-03-12) [umoeller]: added more error checks
  746.  */
  747.  
  748. PSZ eaCreatePSZFromMVBinding(PEABINDING peab,       // in: EAT_MVMT binding
  749.                              const char *pcszSeparator,  // in: null-terminated string used as separator
  750.                              PUSHORT pusCodepage)   // out: codepage found in binding (ptr can be NULL)
  751. {
  752.     PSZ     pszTotal = NULL;     // this will hold the whole string
  753.  
  754.     if (peab)
  755.     {
  756.         USHORT  usEAType = 0;
  757.  
  758.         USHORT usMVCount = eaQueryMVCount(peab,
  759.                                           pusCodepage,
  760.                                           &usEAType);
  761.         if (    (usMVCount)
  762.                     // items found?
  763.              && (usEAType == EAT_MVMT)
  764.                     // rule out EAT_MVST, which we cannot handle here
  765.            )
  766.         {
  767.             // EAT_MVMT items found:
  768.             // go thru all of them
  769.             USHORT  us = 0;
  770.             USHORT  cbComment = 0;
  771.             USHORT  cbSeparator = strlen(pcszSeparator);
  772.             while (us < usMVCount)
  773.             {
  774.                 USHORT  usEATypeThis = 0;
  775.                 USHORT  usEALenThis = 0;
  776.                 PSZ pszSubItem = eaQueryMVItem(peab,
  777.                                                us,    // index
  778.                                                NULL,  // codepage
  779.                                                &usEATypeThis,
  780.                                                &usEALenThis);
  781.                 if (!pszSubItem)
  782.                     break;
  783.  
  784.                 if (usEATypeThis == EAT_ASCII)
  785.                 {
  786.                     if (usEALenThis)
  787.                     {
  788.                         PSZ pszTemp = pszTotal;
  789.                         PSZ pTarget;
  790.                         cbComment += usEALenThis + cbSeparator;
  791.                         pszTotal = (PSZ)malloc(cbComment);
  792.                         if (pszTemp)
  793.                         {
  794.                             // not first loop: append EA value
  795.                             USHORT cbCommentOld = strlen(pszTemp);
  796.                             // copy previous compiled string
  797.                             strcpy(pszTotal, pszTemp);
  798.                             // append separator
  799.                             memcpy(pszTotal + cbCommentOld,
  800.                                    pcszSeparator,
  801.                                    cbSeparator);
  802.                             // copy the rest after the separator (below)
  803.                             pTarget = pszTotal + cbCommentOld + cbSeparator;
  804.                             free(pszTemp);
  805.                         }
  806.                         else
  807.                             // first loop: copy to beginning
  808.                             pTarget = pszTotal;
  809.  
  810.                         // copy EA value
  811.                         memcpy(pTarget, pszSubItem, usEALenThis);
  812.                         // append null terminator
  813.                         *(pTarget + usEALenThis) = 0;
  814.                     }
  815.                 } // if (usEATypeThis == EAT_ASCII)
  816.                 us++;
  817.             } // end while (us < usMVCount)
  818.         } // end if (usMVCount)
  819.     }
  820.  
  821.     return (pszTotal);
  822. }
  823.  
  824. /*
  825.  *@@ eaCreateMVBindingFromPSZ:
  826.  *      reverse to eaCreatePSZFromMVBinding, this
  827.  *      parses pszInput and creates an EAT_MVMT
  828.  *      EABINDING, all of whose subvalues are of
  829.  *      EAT_ASCII. The EABINDING is non-critical
  830.  *      (peab->bFlags = 0).
  831.  *
  832.  *      pszSeparator is used in the analysis of pszInput
  833.  *      as the separator string (e.g. "\r\n").
  834.  *
  835.  *      Returns NULL if the given string is NULL.
  836.  *
  837.  *      <B>Example:</B>
  838.  +          PSZ pszInput = "Line 1\r\nLine2\r\nLine3\r\n";
  839.  +          PEABINDING peab = eaCreateMVBindingFromPSZ(".KEYPHRASES",
  840.  +                                                     pszInput,
  841.  +                                                     "\r\n",
  842.  +                                                     0);      // codepage
  843.  *      will create an EABINDING with the following pszValue:
  844.  +          EAT_MVMT codp usCount (usDataType usLen data...)
  845.  +          EAT_MVMT   0     3    EAT_ASCII     6   Line 1
  846.  +                                EAT_ASCII     5   Line2
  847.  +                                EAT_ASCII     5   Line3
  848.  *
  849.  *@@added V0.9.0 [umoeller]
  850.  */
  851.  
  852. PEABINDING eaCreateMVBindingFromPSZ(const char *pcszEAName,      // in: EA name (e.g. ".KEYPHRASES")
  853.                                     const char *pcszInput,       // in: string to parse
  854.                                     const char *pcszSeparator,   // in: separator used in pszInput
  855.                                     USHORT usCodepage)  // in: codepage to set in EAT_MVMT
  856. {
  857.     PEABINDING peab = NULL;
  858.     if (pcszInput)
  859.     {
  860.         if ((peab = (PEABINDING)malloc(sizeof(EABINDING))))
  861.         {
  862.             const char *p = pcszInput,
  863.                     *pSource;
  864.             PSZ     pTarget;
  865.             USHORT  cbInput = strlen(pcszInput),
  866.                     cbSep = strlen(pcszSeparator),
  867.                     usSepCount = 0,
  868.                     cbToAlloc = 0,
  869.                     cbThis,
  870.                     us;
  871.  
  872.             peab->bFlags = 0;
  873.             peab->bNameLength = strlen(pcszEAName);
  874.             peab->pszName = strdup(pcszEAName);
  875.  
  876.             // now count the number of pszSeparators in pszInput
  877.             while ((p = strstr(p, pcszSeparator)))
  878.             {
  879.                 usSepCount++;
  880.                 p += cbSep;
  881.             }
  882.             // usSepCount now has the separator count; that means
  883.             // we have (usSepCount + 1) data fields
  884.  
  885.             // allocate the memory we need for the total EA:
  886.             // EAT_MVMT usCodepage usCount (usDataType data... )...
  887.  
  888.             cbToAlloc =     6       // six fixed bytes for (EAT_MVMT usCodepage usCount)
  889.                          +  cbInput - (usSepCount * cbSep)
  890.                                     // length of input string minus length of all separators
  891.                          +  ((usSepCount + 1) * 4);
  892.                                     // for each data field (of which we'll have (usSepCount + 1)),
  893.                                     // we need an extra four bytes for EAT_ASCII and the length
  894.             peab->usValueLength = cbToAlloc;
  895.             peab->pszValue = (PSZ)malloc(cbToAlloc + 1);
  896.  
  897.             // now initialize the first three fields:
  898.             *((PUSHORT)(peab->pszValue)) = EAT_MVMT;
  899.             *((PUSHORT)(peab->pszValue + 2)) = usCodepage;
  900.             *((PUSHORT)(peab->pszValue + 4)) = (usSepCount + 1);
  901.  
  902.             // set pointer to first field
  903.             pSource = pcszInput;
  904.             pTarget = peab->pszValue + 6;
  905.  
  906.             // now go thru all fields except the last
  907.             for (us = 0;
  908.                  us < usSepCount;       // exclude the last field
  909.                  us++)
  910.             {
  911.                 // find the next separator
  912.                 PSZ     pNextSep = strstr(pSource, pcszSeparator);
  913.                 // calculate the length of the substring
  914.                 cbThis = pNextSep - pSource;
  915.                 // set data type in field
  916.                 *((PUSHORT)(pTarget)) = EAT_ASCII;
  917.                 // set length of this field
  918.                 *((PUSHORT)(pTarget + 2)) = cbThis;
  919.                 // copy data
  920.                 memcpy(pTarget + 4, pSource, cbThis);
  921.                 // advance source to the next (after substring and separator)
  922.                 pSource += cbThis + cbSep;
  923.                 // advance target to the next (after substring and (EAT_ASCII usLen)
  924.                 pTarget += cbThis + 4;
  925.             }
  926.  
  927.             // now handle the last field
  928.             cbThis = strlen(pSource);
  929.             *((PUSHORT)(pTarget)) = EAT_ASCII;
  930.             *((PUSHORT)(pTarget + 2)) = cbThis;
  931.             memcpy(pTarget + 4, pSource, cbThis);
  932.         } // end if (peab)
  933.     } // end if (pszInput)
  934.  
  935.     return (peab);
  936. }
  937.  
  938. /********************************************************************
  939.  *
  940.  *   EA helper funcs
  941.  *
  942.  ********************************************************************/
  943.  
  944. /*
  945.  *  These get called by the functions above.
  946.  *
  947.  */
  948.  
  949. /*
  950.  *@@ ReadEAList:
  951.  *
  952.  */
  953.  
  954. static PEALIST ReadEAList(ULONG type, // in: ENUMEA_REFTYPE_FHANDLE or  ENUMEA_REFTYPE_PATH
  955.                           PVOID pfile)
  956. {
  957.     ULONG index = 1;
  958.     PEALIST head = 0;
  959.     PEALIST tail = 0;
  960.  
  961.     while (1)
  962.     {
  963.         PEABINDING binding = (ReadEAByIndex(type, pfile, index));
  964.         if (binding == 0)
  965.             break;
  966.         {
  967.             PEALIST list = (PEALIST)malloc(sizeof(EALIST));
  968.             (EA_LIST_BINDING (list)) = binding;
  969.             (EA_LIST_NEXT (list)) = 0;
  970.             if (head == 0)
  971.                 head = list;
  972.             else
  973.                 (EA_LIST_NEXT (tail)) = list;
  974.             tail = list;
  975.         }
  976.         index += 1;
  977.     }
  978.     return (head);
  979. }
  980.  
  981. /*
  982.  *@@ ReadEAByIndex:
  983.  *
  984.  */
  985.  
  986. static PEABINDING ReadEAByIndex(ULONG type, // in: ENUMEA_REFTYPE_FHANDLE or  ENUMEA_REFTYPE_PATH
  987.                                 PVOID pfile,
  988.                                 ULONG index)
  989. {
  990.     PDENA2 dena = (ReadDenaByIndex(type, pfile, index));
  991.     return ((dena == 0)
  992.                     ? 0
  993.                     : (GetEAValue(type, pfile, dena)));
  994. }
  995.  
  996. /*
  997.  *@@ ReadEAByName:
  998.  *
  999.  */
  1000.  
  1001. static PEABINDING ReadEAByName(ULONG type, // in: ENUMEA_REFTYPE_FHANDLE or  ENUMEA_REFTYPE_PATH
  1002.                                PVOID pfile,
  1003.                                PSZ name)
  1004. {
  1005.     ULONG index = 1;
  1006.     while (1)
  1007.     {
  1008.         PDENA2 dena = ReadDenaByIndex(type, pfile, index);
  1009.         if (dena == 0)
  1010.             return NULL;
  1011.         if ((strcmp(name, (dena->szName))) == 0)
  1012.             return (GetEAValue(type, pfile, dena));
  1013.         free(dena);
  1014.         index += 1;
  1015.     }
  1016. }
  1017.  
  1018. /*
  1019.  *@@ ReadDenaByIndex:
  1020.  *      fills a DENA2 structure by calling DosEnumAttribute.
  1021.  *
  1022.  *      DENA2 is typedef'd to FEA2 in the OS/2 headers.
  1023.  *      FEA2 defines the format for setting the full extended
  1024.  *      attributes in the file.
  1025.  *
  1026.  +         typedef struct _FEA2 {
  1027.  +           ULONG      oNextEntryOffset;  //  Offset to next entry.
  1028.  +           BYTE       fEA;               //  Extended attributes flag.
  1029.  +           BYTE       cbName;            //  Length of szName, not including NULL.
  1030.  +           USHORT     cbValue;           //  Value length.
  1031.  +           CHAR       szName[1];         //  Extended attribute name.
  1032.  +         } FEA2;
  1033.  +         typedef FEA2 *PFEA2;
  1034.  *
  1035.  *      Returns 0 on errors.
  1036.  *
  1037.  *@@changed V0.9.2 (2000-03-12) [umoeller]: added more error checking
  1038.  *@@changed V0.9.4 (2000-08-03) [umoeller]: added even more error checking; this fixed problems with swapper.dat and such
  1039.  */
  1040.  
  1041. static PDENA2 ReadDenaByIndex(ULONG type, // in: ENUMEA_REFTYPE_FHANDLE or  ENUMEA_REFTYPE_PATH
  1042.                               PVOID pfile, // in: file handle or name
  1043.                               ULONG index) // in: EA index (>= 1)
  1044. {
  1045.     ULONG count = 1;
  1046.     PDENA2 dena = (PDENA2)malloc(500); // 500 is magic -- IBM doesn't explain.
  1047.     APIRET arc = DosEnumAttribute(type,
  1048.                                   pfile,
  1049.                                   index,
  1050.                                   dena,
  1051.                                   500,
  1052.                                   (&count),
  1053.                                   ENUMEA_LEVEL_NO_VALUE);
  1054.     if (   (arc != NO_ERROR)
  1055.         || (count == 0)     // check error code?
  1056.        )
  1057.     {
  1058.         free(dena);
  1059.         return (0);
  1060.     }
  1061.     else
  1062.         return (dena);
  1063. }
  1064.  
  1065. /*
  1066.  *@@ GetEAValue:
  1067.  *
  1068.  *@@changed V0.9.1 (2000-01-30) [umoeller]: now returning NULL upon errors
  1069.  */
  1070.  
  1071. static PEABINDING GetEAValue(ULONG type,  // in: ENUMEA_REFTYPE_FHANDLE or  ENUMEA_REFTYPE_PATH
  1072.                              PVOID pfile, // in: file handle or path
  1073.                              PDENA2 dena) // in: buffer allocated by ReadDenaByIndex
  1074. {
  1075.     ULONG level = FIL_QUERYEASFROMLIST;
  1076.     EAOP2 eaop;
  1077.     ULONG size = (sizeof(eaop));
  1078.     APIRET arc = NO_ERROR;
  1079.     SetupQueryEAInfo(dena, (&eaop));
  1080.             // this freeds dena
  1081.     if (type == ENUMEA_REFTYPE_FHANDLE)
  1082.         arc = DosQueryFileInfo((* ((PHFILE) pfile)), level, (&eaop), size);
  1083.     else
  1084.         arc = DosQueryPathInfo((PSZ)pfile, level, (&eaop), size);
  1085.                     // changed V0.9.0 (99-11-28) [umoeller]; this now works with C
  1086.     free(eaop.fpGEA2List);
  1087.     if (arc == NO_ERROR)
  1088.         return (ConvertFeal2Binding(eaop.fpFEA2List));
  1089.     else
  1090.         return NULL;
  1091. }
  1092.  
  1093. /*
  1094.  *@@ SetupQueryEAInfo:
  1095.  *      prepares a EAOP2 structure for querying EAs.
  1096.  *      This also frees dena, since it's no longer used.
  1097.  */
  1098.  
  1099. static void SetupQueryEAInfo(PDENA2 dena,  // in: buffer allocated by ReadDenaByIndex
  1100.                              PEAOP2 eaop)  // out: EAOP2 to fill
  1101. {
  1102.     unsigned int geal_size = ((sizeof (GEA2LIST)) + (dena->cbName));
  1103.     unsigned int feal_size
  1104.                  = ((sizeof (FEA2LIST)) + (dena->cbName) + (dena->cbValue));
  1105.     (eaop->fpGEA2List) = (GEA2LIST*)(malloc(geal_size));
  1106.     ((eaop->fpGEA2List)->cbList) = geal_size;
  1107.     (eaop->fpFEA2List) = (FEA2LIST*)(malloc(feal_size));
  1108.     ((eaop->fpFEA2List)->cbList) = feal_size;
  1109.     (eaop->oError) = 0;
  1110.     {
  1111.         PGEA2 gea = (&(((eaop->fpGEA2List)->list) [0]));
  1112.         (gea->oNextEntryOffset) = 0;
  1113.         (gea->cbName) = (dena->cbName);
  1114.         strcpy ((gea->szName), (dena->szName));
  1115.     }
  1116.     free(dena);
  1117. }
  1118.  
  1119. /*
  1120.  *@@ ConvertFeal2Binding:
  1121.  *      creates a new EABINDING from the given FEA2LIST
  1122.  *      and frees feal. Returns NULL upon errors.
  1123.  *
  1124.  *@@changed V0.9.1 (2000-01-30) [umoeller]: now returning NULL upon errors
  1125.  */
  1126.  
  1127. static PEABINDING ConvertFeal2Binding(PFEA2LIST feal)
  1128. {
  1129.     PFEA2 fea = (&((feal->list) [0]));
  1130.     PEABINDING binding = (PEABINDING)(malloc(sizeof (EABINDING)));
  1131.     if (binding)
  1132.     {
  1133.         (EA_BINDING_FLAGS (binding)) = (fea->fEA);
  1134.         (EA_BINDING_NAME_LENGTH (binding)) = (fea->cbName);
  1135.         (EA_BINDING_VALUE_LENGTH (binding)) = (fea->cbValue);
  1136.         (EA_BINDING_NAME (binding)) = (PSZ)(malloc((fea->cbName) + 1));
  1137.         strcpy ((EA_BINDING_NAME (binding)), (fea->szName));
  1138.         (EA_BINDING_VALUE (binding)) = (PSZ)(malloc(fea->cbValue));
  1139.         memcpy ((EA_BINDING_VALUE (binding)),
  1140.                 (&((fea->szName) [(fea->cbName) + 1])),
  1141.                 (fea->cbValue));
  1142.         free(feal);
  1143.     }
  1144.     return (binding);
  1145. }
  1146.  
  1147. /*
  1148.  *@@ WriteEAList:
  1149.  *
  1150.  *@@changed V0.9.1 (2000-01-30) [umoeller]: now returning APIRET
  1151.  */
  1152.  
  1153. static APIRET WriteEAList(ULONG type, // in: ENUMEA_REFTYPE_FHANDLE or  ENUMEA_REFTYPE_PATH
  1154.                           PVOID pfile,
  1155.                           PEALIST list)
  1156. {
  1157.     APIRET arc = NO_ERROR;
  1158.     while (    (list != 0)
  1159.             && (arc == NO_ERROR)
  1160.           )
  1161.     {
  1162.         arc = WriteEA(type, pfile, (EA_LIST_BINDING (list)));
  1163.         list = (EA_LIST_NEXT (list));
  1164.     }
  1165.  
  1166.     return arc;
  1167. }
  1168.  
  1169. /*
  1170.  *@@ WriteEA:
  1171.  *
  1172.  *@@changed V0.9.1 (2000-01-30) [umoeller]: now returning APIRET
  1173.  */
  1174.  
  1175. static APIRET WriteEA(ULONG type,          // in: ENUMEA_REFTYPE_FHANDLE or  ENUMEA_REFTYPE_PATH
  1176.                       PVOID pfile,
  1177.                       PEABINDING binding)
  1178. {
  1179.     ULONG level = FIL_QUERYEASIZE;
  1180.     EAOP2 eaop;
  1181.     ULONG size = (sizeof (eaop));
  1182.     APIRET arc = NO_ERROR;
  1183.  
  1184.     (eaop.fpGEA2List) = 0;
  1185.     (eaop.fpFEA2List) = (ConvertBinding2Feal(binding));
  1186.     (eaop.oError) = 0;
  1187.     if (type == ENUMEA_REFTYPE_FHANDLE)
  1188.         arc = DosSetFileInfo((* ((PHFILE) pfile)), level, (&eaop), size);
  1189.     else
  1190.         arc = DosSetPathInfo((PSZ)pfile, level, (&eaop), size, DSPI_WRTTHRU);
  1191.     free(eaop.fpFEA2List);
  1192.     return arc;
  1193. }
  1194.  
  1195. /*
  1196.  *@@ ConvertBinding2Feal:
  1197.  *
  1198.  */
  1199.  
  1200. static PFEA2LIST ConvertBinding2Feal(PEABINDING binding)
  1201. {
  1202.     unsigned int feal_size
  1203.               = ((sizeof (FEA2LIST))
  1204.                  + (EA_BINDING_NAME_LENGTH (binding))
  1205.                  + (EA_BINDING_VALUE_LENGTH (binding)));
  1206.     PFEA2LIST feal = (PFEA2LIST)(malloc(feal_size));
  1207.     PFEA2 fea = (&((feal->list) [0]));
  1208.     (feal->cbList) = feal_size;
  1209.     (fea->oNextEntryOffset) = 0;
  1210.     (fea->fEA) = (EA_BINDING_FLAGS (binding));
  1211.     (fea->cbName) = (EA_BINDING_NAME_LENGTH (binding));
  1212.     (fea->cbValue) = (EA_BINDING_VALUE_LENGTH (binding));
  1213.     strcpy ((fea->szName), (EA_BINDING_NAME (binding)));
  1214.     if ((EA_BINDING_VALUE (binding)) != 0)
  1215.         memcpy ((&((fea->szName) [(fea->cbName) + 1])),
  1216.                 (EA_BINDING_VALUE (binding)),
  1217.                 (fea->cbValue));
  1218.     return (feal);
  1219. }
  1220.  
  1221. /* ******************************************************************
  1222.  *
  1223.  *   Direct plain-string EA handling
  1224.  *
  1225.  ********************************************************************/
  1226.  
  1227. #define USE_EAMVMT 1
  1228.  
  1229. /*
  1230.  * eahWriteStringEA:
  1231.  *
  1232.  *      (C) Christian Langanke. Moved this here from tmsgfile.c.
  1233.  *
  1234.  *@@added V0.9.3 (2000-05-21) [umoeller]
  1235.  */
  1236.  
  1237. APIRET eahWriteStringEA(HFILE hfile,
  1238.                         PSZ pszEaName,
  1239.                         PSZ pszEaValue)
  1240. {
  1241.  
  1242.     APIRET          rc = NO_ERROR;
  1243.     PFEA2LIST       pfea2l = NULL;
  1244.  
  1245. #ifdef USE_EAMVMT
  1246.     PEAMVMT         peamvmt;
  1247. #else
  1248.     PEASVST         peasvst;
  1249. #endif
  1250.     ULONG           ulEAListLen;
  1251.     ULONG           ulValueLen;
  1252.     EAOP2           eaop2;
  1253.  
  1254.     // PSZ             pszValue;
  1255.  
  1256.     do
  1257.     {
  1258.         // check parameters
  1259.         if (    (hfile == NULLHANDLE)
  1260.              || (pszEaName == NULL)
  1261.              || (*pszEaName == 0)
  1262.              || (pszEaValue == NULL)
  1263.            )
  1264.         {
  1265.             rc = ERROR_INVALID_PARAMETER;
  1266.             break;
  1267.         }
  1268.  
  1269.         // write EAs
  1270.         ulValueLen = strlen(pszEaValue) +
  1271. #ifdef USE_EAMVMT
  1272.             sizeof(EAMVMT);
  1273. #else
  1274.             sizeof(EASVST);
  1275. #endif
  1276.  
  1277.         ulEAListLen = strlen(pszEaName) +
  1278.                       sizeof(FEA2LIST) +
  1279.                       ulValueLen;
  1280.  
  1281.         // get memory for FEA2LIST
  1282.         pfea2l = (FEA2LIST*)malloc(ulEAListLen);
  1283.         if (pfea2l == 0)
  1284.         {
  1285.             rc = ERROR_NOT_ENOUGH_MEMORY;
  1286.             break;
  1287.         }
  1288.  
  1289.         // init FEA2LIST
  1290.         eaop2.fpGEA2List = NULL;
  1291.         eaop2.fpFEA2List = pfea2l;
  1292.         memset(pfea2l, 0, ulEAListLen);
  1293.  
  1294.         // write timeframe EA
  1295.         pfea2l->cbList = ulEAListLen;
  1296.         pfea2l->list[0].cbName = strlen(pszEaName);
  1297.         strcpy(pfea2l->list[0].szName, pszEaName);
  1298.  
  1299.         // delete attribute if value empty
  1300.         if (strlen(pszEaValue) == 0)
  1301.             pfea2l->list[0].cbValue = 0;
  1302.         else
  1303.         {
  1304.             pfea2l->list[0].cbValue = ulValueLen;
  1305.  
  1306. #ifdef USE_EAMVMT
  1307.             // multi value multi type
  1308.             peamvmt = (PEAMVMT) NEXTSTR(pfea2l->list[0].szName);
  1309.             peamvmt->usType = EAT_MVMT;
  1310.             peamvmt->usCodepage = 0;
  1311.             peamvmt->usEntries = 1;
  1312.             peamvmt->usEntryType = EAT_ASCII;
  1313.             peamvmt->usEntryLen = strlen(pszEaValue);
  1314.             memcpy(&peamvmt->chEntry[0], pszEaValue, peamvmt->usEntryLen);
  1315. #else
  1316.             // single value single type
  1317.             peasvst = NEXTSTR(pfea2l->list[0].szName);
  1318.             peasvst->usType = EAT_ASCII;
  1319.             peasvst->usEntryLen = strlen(pszEaValue);
  1320.             memcpy(&peasvst->chEntry[0], pszEaValue, peasvst->usEntryLen);
  1321. #endif
  1322.         }
  1323.  
  1324.         // set the new EA value
  1325.         rc = DosSetFileInfo(hfile,
  1326.                             FIL_QUERYEASIZE,
  1327.                             &eaop2,
  1328.                             sizeof(eaop2));
  1329.  
  1330.     }
  1331.     while (FALSE);
  1332.  
  1333. // cleanup
  1334.     if (pfea2l)
  1335.         free(pfea2l);
  1336.     return rc;
  1337. }
  1338.  
  1339. /*
  1340.  * eahReadStringEA:
  1341.  *
  1342.  *      (C) Christian Langanke. Moved this here from tmsgfile.c.
  1343.  *
  1344.  *@@added V0.9.3 (2000-05-21) [umoeller]
  1345.  */
  1346.  
  1347. APIRET eahReadStringEA(PSZ pszFileName,
  1348.                        PSZ pszEaName,
  1349.                        PSZ pszBuffer,
  1350.                        PULONG pulBuflen)
  1351. {
  1352.  
  1353.     APIRET          rc = NO_ERROR;
  1354.     FILESTATUS4     fs4;
  1355.  
  1356.     EAOP2           eaop2;
  1357.     PGEA2LIST       pgea2l = NULL;
  1358.     PFEA2LIST       pfea2l = NULL;
  1359.  
  1360.     PGEA2           pgea2;
  1361.     PFEA2           pfea2;
  1362.  
  1363.     ULONG           ulGea2Len = 0;
  1364.     ULONG           ulFea2Len = 0;
  1365.  
  1366.     PEASVST         peasvst;
  1367.     PEAMVMT         peamvmt;
  1368.  
  1369.     ULONG           ulRequiredLen;
  1370.  
  1371.     do
  1372.     {
  1373.         // check parameters
  1374.         if ((pszFileName == NULL) ||
  1375.             (pszEaName == NULL) ||
  1376.             (*pszEaName == 0) ||
  1377.             (pulBuflen == NULL))
  1378.         {
  1379.             rc = ERROR_INVALID_PARAMETER;
  1380.             break;
  1381.         }
  1382.  
  1383.         // initialize target buffer
  1384.         if (pszBuffer)
  1385.             memset(pszBuffer, 0, *pulBuflen);
  1386.  
  1387.         // get EA size
  1388.         rc = DosQueryPathInfo(pszFileName,
  1389.                               FIL_QUERYEASIZE,
  1390.                               &fs4,
  1391.                               sizeof(fs4));
  1392.         if (rc != NO_ERROR)
  1393.             break;
  1394.  
  1395.         // no eas here ?
  1396.         if (fs4.cbList == 0)
  1397.         {
  1398.             pulBuflen = 0;
  1399.             break;
  1400.         }
  1401.  
  1402.         // determine required space
  1403.         // - for ulFea2Len use at least 2 * Gea2Len because
  1404.         //   buffer needs at least to be Geal2Len even for an empty
  1405.         //   attribute, otherwise rc == ERROR_BUFFER_OVERFLOW !
  1406.         ulGea2Len = sizeof(GEA2LIST) + strlen(pszEaName);
  1407.         ulFea2Len = 2 * MAX(fs4.cbList, ulGea2Len);
  1408.  
  1409.         // get memory for GEA2LIST
  1410.         if ((pgea2l = (GEA2LIST*)malloc(ulGea2Len)) == 0)
  1411.         {
  1412.             rc = ERROR_NOT_ENOUGH_MEMORY;
  1413.             break;
  1414.         }
  1415.         memset(pgea2l, 0, ulGea2Len);
  1416.  
  1417.         // get memory for FEA2LIST
  1418.         if ((pfea2l = (FEA2LIST*)malloc(ulFea2Len)) == 0)
  1419.         {
  1420.             rc = ERROR_NOT_ENOUGH_MEMORY;
  1421.             break;
  1422.         }
  1423.         memset(pfea2l, 0, ulFea2Len);
  1424.  
  1425.         // init ptrs and do the query
  1426.         memset(&eaop2, 0, sizeof(EAOP2));
  1427.         eaop2.fpGEA2List = pgea2l;
  1428.         eaop2.fpFEA2List = pfea2l;
  1429.         pfea2l->cbList = ulFea2Len;
  1430.         pgea2l->cbList = ulGea2Len;
  1431.  
  1432.         pgea2 = &pgea2l->list[0];
  1433.         pfea2 = &pfea2l->list[0];
  1434.  
  1435.  
  1436.         pgea2->oNextEntryOffset = 0;
  1437.         pgea2->cbName = strlen(pszEaName);
  1438.         strcpy(pgea2->szName, pszEaName);
  1439.  
  1440.         rc = DosQueryPathInfo(pszFileName,
  1441.                               FIL_QUERYEASFROMLIST,
  1442.                               &eaop2,
  1443.                               sizeof(eaop2));
  1444.         if (rc != NO_ERROR)
  1445.             break;
  1446.  
  1447.         // check first entry only
  1448.         peamvmt = (PEAMVMT) ((PBYTE) pfea2->szName + pfea2->cbName + 1);
  1449.  
  1450.         // is it MVMT ? then adress single EA !
  1451.         if (peamvmt->usType == EAT_MVMT)
  1452.         {
  1453.             peasvst = (PEASVST) & peamvmt->usEntryType;
  1454.         }
  1455.         else
  1456.             peasvst = (PEASVST) peamvmt;
  1457.  
  1458.  
  1459.         // is entry empty ?
  1460.         if (peasvst->usEntryLen == 0)
  1461.         {
  1462.             rc = ERROR_INVALID_EA_NAME;
  1463.             break;
  1464.         }
  1465.  
  1466.         // is it ASCII ?
  1467.         if (peasvst->usType != EAT_ASCII)
  1468.         {
  1469.             rc = ERROR_INVALID_DATA;
  1470.             break;
  1471.         }
  1472.  
  1473.         // check buffer and hand over value
  1474.         ulRequiredLen = peasvst->usEntryLen + 1;
  1475.         if (*pulBuflen < ulRequiredLen)
  1476.         {
  1477.             *pulBuflen = ulRequiredLen;
  1478.             rc = ERROR_BUFFER_OVERFLOW;
  1479.             break;
  1480.         }
  1481.  
  1482.         // hand over len
  1483.         *pulBuflen = ulRequiredLen;
  1484.  
  1485.         // hand over value
  1486.         if (pszBuffer)
  1487.             memcpy(pszBuffer, peasvst->chEntry, peasvst->usEntryLen);
  1488.  
  1489.     }
  1490.     while (FALSE);
  1491.  
  1492. // cleanup
  1493.     if (pgea2l)
  1494.         free(pgea2l);
  1495.     if (pfea2l)
  1496.         free(pfea2l);
  1497.     return rc;
  1498. }
  1499.  
  1500.  
  1501.