home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / xwphescr.zip / XWPH0208.ZIP / src / helpers / level.c < prev    next >
C/C++ Source or Header  |  2001-01-08  |  26KB  |  842 lines

  1.  
  2. /*
  3.  *@@sourcefile level.c:
  4.  *      contains helper functions for querying SYSLEVEL files.
  5.  *
  6.  *      This is all new with V0.9.2.
  7.  *
  8.  *      Function prefixes:
  9.  *      --  lvl*    syslevel helpers
  10.  *
  11.  *@@added V0.9.2 (2000-03-08) [umoeller]
  12.  *@@header "helpers\level.h"
  13.  */
  14.  
  15. /*
  16.  *      Copyright (C) 1994 Martin Lafaix (EDM/2 2-05).
  17.  *      Copyright (C) 2000 Ulrich Möller.
  18.  *      This file is part of the "XWorkplace helpers" source package.
  19.  *      This is free software; you can redistribute it and/or modify
  20.  *      it under the terms of the GNU General Public License as published
  21.  *      by the Free Software Foundation, in version 2 as it comes in the
  22.  *      "COPYING" file of the XWorkplace main distribution.
  23.  *      This program is distributed in the hope that it will be useful,
  24.  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
  25.  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  26.  *      GNU General Public License for more details.
  27.  */
  28.  
  29. #define OS2EMX_PLAIN_CHAR
  30.     // this is needed for "os2emx.h"; if this is defined,
  31.     // emx will define PSZ as _signed_ char, otherwise
  32.     // as unsigned char
  33.  
  34. #define INCL_DOSFILEMGR
  35. #include <os2.h>
  36.  
  37. #include <stdlib.h>
  38. #include <string.h>
  39.  
  40. #include "setup.h"
  41.  
  42. #define SYSLEVEL_PRIVATE
  43. #include "helpers\level.h"
  44.  
  45. // redefine the FIELDOFFSET macro; the one
  46. // in the OS/2 header files doesn't work with C++
  47. #undef FIELDOFFSET
  48. #define FIELDOFFSET(type, field)    ((ULONG)&(((type *)0)->field))
  49.  
  50. /*
  51.  *@@category: Helpers\Control program helpers\SYSLEVEL parsing
  52.  *      helpers for parsing SYSLEVEL files. See lvlOpenLevelFile.
  53.  */
  54.  
  55. /* ******************************************************************
  56.  *
  57.  *   Interface
  58.  *
  59.  ********************************************************************/
  60.  
  61. /*
  62.  *@@ lvlOpenLevelFile:
  63.  *      finds, opens, or creates a SYSLEVEL file. *phFile
  64.  *      receives the DosOpen file handle, which can then be
  65.  *      passed to lvlQueryLevelFileData and such. Alternatively,
  66.  *      you can use DosOpen directly.
  67.  *
  68.  *      ulOpenMode can be:
  69.  *
  70.  *      -- OLF_OPEN:
  71.  *              open the specified file. The default behavior is to scan
  72.  *              the current disk, starting from the current directory.
  73.  *              It returns the first SYSLEVEL file whose extension
  74.  *              matches pszName. You can override this by
  75.  *              combining OLF_OPEN with OLF_SCANDISK or
  76.  *              OLF_CHECKID.
  77.  *
  78.  *      -- OLF_SCANDISKS:
  79.  *              scans all disks (starting from C:). Use this flag to
  80.  *              modify the default OLF_OPEN behavior.
  81.  *
  82.  *      -- OLF_CHECKID:
  83.  *              finds file(s) whose ID matches the specified one. Use
  84.  *              this flag to override the default OLF_OPEN behavior.
  85.  *
  86.  *      -- OLF_CREATE:
  87.  *              creates the specified file in the current directory. A
  88.  *              valid pszName and ID should be provided.
  89.  */
  90.  
  91. APIRET lvlOpenLevelFile(PSZ pszName,        // in: syslevel file name extension
  92.                         PHFILE phFile,      // out: Dos file handle
  93.                         ULONG ulOpenMode,   // in: OLF_* flags
  94.                         PSZ pszCID)         // in: if OLF_CHECKID is specified, the ID
  95. {
  96.     APIRET rc = 0;
  97.     ULONG ulAction;
  98.     CHAR achFileName[CCHMAXPATHCOMP];
  99.  
  100.     switch (ulOpenMode)
  101.     {
  102.         case OLF_OPEN:
  103.         case OLF_OPEN | OLF_CHECKID:
  104.             if (pszName || pszCID)
  105.             {
  106.                 rc = _findsyslevelfile(pszName, pszCID, achFileName);
  107.  
  108.                 if (rc == 18)
  109.                     rc = _searchsubdirs(pszName, pszCID, achFileName);
  110.  
  111.                 if (rc == 0)
  112.                     rc = DosOpen(achFileName,
  113.                                  phFile,
  114.                                  &ulAction,
  115.                                  0,
  116.                                  FILE_NORMAL,
  117.                                  FILE_OPEN,
  118.                                  OPEN_ACCESS_READWRITE | OPEN_SHARE_DENYWRITE,
  119.                                  0);
  120.  
  121.                 if (rc == 18)
  122.                     rc = 2;
  123.             }
  124.             else
  125.                 rc = ERROR_INVALID_PARAMETER;
  126.             break;
  127.  
  128.         case OLF_SCANDISKS:
  129.         case OLF_SCANDISKS | OLF_CHECKID:
  130.         case OLF_SCANDISKS | OLF_OPEN:
  131.         case OLF_SCANDISKS | OLF_OPEN | OLF_CHECKID:
  132.             if (pszName || pszCID)
  133.             {
  134.                 ULONG ulDrive, ulDriveMap, ulCurrent, ulFound = 0;
  135.  
  136.                 rc = DosQueryCurrentDisk(&ulDrive, &ulDriveMap);
  137.                 ulCurrent = _firstdrive(ulDriveMap);
  138.  
  139.                 while (ulCurrent && !ulFound)
  140.                 {
  141.                     rc = _findsyslevelfile(pszName, pszCID, achFileName);
  142.                     if (rc == 18)
  143.                         rc = _searchsubdirs(pszName, pszCID, achFileName);
  144.  
  145.                     if (rc == 0)
  146.                     {
  147.                         ulFound = 1;
  148.  
  149.                         rc = DosOpen(achFileName,
  150.                                      phFile,
  151.                                      &ulAction,
  152.                                      0,
  153.                                      FILE_NORMAL,
  154.                                      FILE_OPEN,
  155.                                      OPEN_ACCESS_READWRITE | OPEN_SHARE_DENYWRITE,
  156.                                      0);
  157.                     }
  158.                     else
  159.                         ulCurrent = _nextdrive(ulDriveMap, ulCurrent);
  160.                 }
  161.  
  162.                 DosSetDefaultDisk(ulDrive);
  163.                 if (ulFound == 0)
  164.                     rc = 2;
  165.             }
  166.             else
  167.                 rc = ERROR_INVALID_PARAMETER;
  168.             break;
  169.  
  170.         case OLF_CREATE:
  171.             if (pszName && pszCID)
  172.             {
  173.                 SYSLEVELHEADER slh;
  174.                 SYSLEVELDATA sld;
  175.  
  176.                 strcat(strcpy(achFileName, "syslevel."), pszName);
  177.  
  178.                 rc = DosOpen(achFileName,
  179.                              phFile,
  180.                              &ulAction,
  181.                              0,
  182.                              FILE_NORMAL,
  183.                              FILE_CREATE,
  184.                              OPEN_ACCESS_READWRITE | OPEN_SHARE_DENYWRITE,
  185.                              0);
  186.  
  187.                 slh.h_magic[0] = slh.h_magic[1] = 0xFF;
  188.                 strcpy((PSZ)slh.h_name, "SYSLEVEL");
  189.                 for (ulAction = 0; ulAction < sizeof(slh.h_reserved1); ulAction++)
  190.                     slh.h_reserved1[ulAction] = 0;
  191.                 slh.h_updated = 0;
  192.                 for (ulAction = 0; ulAction < sizeof(slh.h_reserved2); ulAction++)
  193.                     slh.h_reserved2[ulAction] = 0;
  194.                 slh.h_data = sizeof(SYSLEVELHEADER);
  195.  
  196.                 if (rc == 0)
  197.                     rc = _writesyslevelheader(*phFile, &slh);
  198.  
  199.                 memset(&sld, 0, sizeof(sld));
  200.                 sld.d_kind[0] = SLK_STANDARD;
  201.                 sld.d_reserved3 = sld.d_reserved4 = 0x5F;
  202.                 memcpy(sld.d_cid, pszCID, sizeof(sld.d_cid));
  203.  
  204.                 if (rc == 0)
  205.                     rc = DosWrite(*phFile, &sld, sizeof(SYSLEVELDATA), &ulAction);
  206.             }
  207.             else
  208.                 rc = ERROR_INVALID_PARAMETER;
  209.             break;
  210.         default:
  211.             rc = ERROR_INVALID_PARAMETER;
  212.             break;
  213.     }
  214.  
  215.     return rc;
  216. }
  217.  
  218. /*
  219.  *@@ lvlQueryLevelFile:
  220.  *      finds all SYSLEVEL files on the system and
  221.  *      writes them to pBuffer.
  222.  */
  223.  
  224. APIRET lvlQueryLevelFile(PSZ pszName,
  225.                          PSZ pszCID,
  226.                          PVOID pBuffer,
  227.                          ULONG ulBufSize)
  228. {
  229.     ULONG ulDrive, ulDriveMap, ulCurrent;
  230.     CHAR achFileName[CCHMAXPATHCOMP];
  231.     APIRET rc = DosQueryCurrentDisk(&ulDrive, &ulDriveMap);
  232.  
  233.     strcat(strcpy(achFileName, "syslevel."),
  234.            (pszName == NULL) ? (PSZ) "???" : pszName);
  235.     memset(pBuffer, 0, ulBufSize);
  236.  
  237.     ulCurrent = _firstdrive(ulDriveMap);
  238.  
  239.     while (ulCurrent && (rc == 0))
  240.     {
  241.         rc = _findallsyslevelfiles(achFileName, pszCID, &pBuffer, &ulBufSize);
  242.  
  243.         if (rc == 0)
  244.             ulCurrent = _nextdrive(ulDriveMap, ulCurrent);
  245.     }
  246.  
  247.     if (rc == 0)
  248.         rc = DosSetDefaultDisk(ulDrive);
  249.  
  250.     return rc;
  251. }
  252.  
  253. /*
  254.  *@@ lvlQueryLevelFileData:
  255.  *      parses syslevel data from a SYSLEVEL file previously
  256.  *      opened with lvlOpenLevelFile. Alternatively, you can
  257.  *      open the SYSLEVEL file directly using DosOpen.
  258.  *
  259.  *      ulWhat can be:
  260.  *
  261.  *      -- QLD_MAJORVERSION
  262.  *              Query (or update) the major version field. It's a
  263.  *              one-character field. It should be in the range '0'-'9'.
  264.  *              The value is placed in (or taken from) the first
  265.  *              character of pBuffer. (Buffer size should be at least 1.)
  266.  *
  267.  *      -- QLD_MINORVERSION
  268.  *              Query (or update) the minor version field. It's a
  269.  *              two-character field. It should be in range '00'-'99'. The
  270.  *              value is placed in (or taken from) the first two chars
  271.  *              of pBuffer. (Buffer size should be at least 2.)
  272.  *
  273.  *      -- QLD_REVISION
  274.  *              Query (or update) the revision field. It's should fit in
  275.  *              a character. If it's '0', there's no revision available. It
  276.  *              can be a letter as well as a digit. The value is placed
  277.  *              in (or taken from) the first character of pBuffer.
  278.  *              (Buffer size should be at least 1.)
  279.  *
  280.  *      -- QLD_KIND
  281.  *              Query (or update) the kind field. The value is placed
  282.  *              in (or taken from) the first character of *pBuffer.
  283.  *              (Buffer size should be at least 1.)
  284.  *
  285.  *      -- QLD_CURRENTCSD
  286.  *              Query (or update) the current CSD level (when you
  287.  *              update this field, its old value is copied to the old
  288.  *              CSD level field). It's a seven-character field, and it
  289.  *              does not have to be null-terminated. The value is
  290.  *              placed in (or taken from) the first seven characters of
  291.  *              pBuffer. (Buffer size should be at least 7.)
  292.  *
  293.  *      -- QLD_PREVIOUSCSD
  294.  *              Query the previous CSD level. You can't update this
  295.  *              field. The value is placed in the first seven chars of
  296.  *              pBuffer. (Buffer size should be at least 7.)
  297.  *              Note: CSD levels are not null-terminated. Be careful
  298.  *              when using such a returned value.
  299.  *
  300.  *      -- QLD_TITLE
  301.  *              Query (or update) the component title field. It's an
  302.  *              eighty-character string (required ending null
  303.  *              included). The value is placed in (or taken from) the
  304.  *              first eighty characters of pBuffer. On input, the buffer
  305.  *              size should be at least 80. On output, the buffer size
  306.  *              can exceed 80, but the written string is truncated
  307.  *              (and null-terminated) to eighty characters.
  308.  *
  309.  *      -- QLD_ID
  310.  *              Query (or update) the component ID field. It's a
  311.  *              nine-character field. It does not have to be
  312.  *              null-terminated. The value is placed in (or taken
  313.  *              from) the first nine characters of pBuffer. (Buffer size
  314.  *              should be at least 9.)
  315.  *              Note: IDs are not null-terminated. Be careful when
  316.  *              using such a returned value.
  317.  *
  318.  *      -- QLD_TYPE
  319.  *              Query (or update) the component type field.
  320.  */
  321.  
  322. APIRET lvlQueryLevelFileData(HFILE hFile,       // in: file handle returned by lvlOpenLevelFile
  323.                              ULONG ulWhat,      // in: QLD* flags
  324.                              PVOID pBuffer,     // in/out: data
  325.                              ULONG ulBufSize,   // in: sizeof(*pBuffer)
  326.                              PULONG pulSize)    // out: size required if
  327.                                                 // ERROR_INSUFFICIENT_BUFFER is returned;
  328.                                                 // otherwise bytes written
  329. {
  330.     APIRET rc = _locatesysleveldata(hFile);
  331.     PSYSLEVELDATA psld = _allocsysleveldata(hFile);
  332.  
  333.     if (rc == 0)
  334.         rc = pBuffer ? rc : ERROR_INVALID_PARAMETER;
  335.     if (rc == 0)
  336.         rc = _readsysleveldata(hFile, psld);
  337.  
  338.     if (rc == 0)
  339.         switch (ulWhat)
  340.         {
  341.             case QLD_MAJORVERSION:
  342.                 if (ulBufSize >= 1)
  343.                     _achBuffer[0] = '0' + (psld->d_version[0] >> 4);
  344.                 else
  345.                     rc = ERROR_INSUFFICIENT_BUFFER;
  346.                 _setsize(1);
  347.                 break;
  348.             case QLD_MINORVERSION:
  349.                 if (ulBufSize >= 2)
  350.                 {
  351.                     _achBuffer[0] = '0' + (psld->d_version[0] & 0x0F);
  352.                     _achBuffer[1] = '0' + psld->d_version[1];
  353.                 }
  354.                 else
  355.                     rc = ERROR_INSUFFICIENT_BUFFER;
  356.                 _setsize(2);
  357.                 break;
  358.             case QLD_REVISION:
  359.                 if (ulBufSize >= 1)
  360.                     _achBuffer[0] = '0' + psld->d_revision[0];
  361.                 else
  362.                     rc = ERROR_INSUFFICIENT_BUFFER;
  363.                 _setsize(1);
  364.                 break;
  365.             case QLD_KIND:
  366.                 _getfield(d_kind);
  367.                 break;
  368.             case QLD_CURRENTCSD:
  369.                 _getfield(d_clevel);
  370.                 break;
  371.             case QLD_PREVIOUSCSD:
  372.                 _getfield(d_plevel);
  373.                 break;
  374.             case QLD_TITLE:
  375.                 _getfield(d_title);
  376.                 break;
  377.             case QLD_ID:
  378.                 _getfield(d_cid);
  379.                 break;
  380.             case QLD_TYPE:
  381.                 if (ulBufSize >= strlen((PSZ)psld->d_type))
  382.                     memcpy(pBuffer, psld->d_type, strlen((PSZ)psld->d_type));
  383.                 else
  384.                     rc = ERROR_INSUFFICIENT_BUFFER;
  385.                 _setsize(strlen((PSZ)psld->d_type));
  386.                 break;
  387.             default:
  388.                 rc = ERROR_INVALID_PARAMETER;
  389.                 break;
  390.         }
  391.  
  392.     _freesysleveldata(psld);
  393.  
  394.     return rc;
  395. }
  396.  
  397. /*
  398.  *@@ lvlWriteLevelFileData:
  399.  *      writes data to a SYSLEVEL file.
  400.  */
  401.  
  402. APIRET lvlWriteLevelFileData(HFILE hFile,
  403.                              ULONG ulWhat,
  404.                              PVOID pBuffer,
  405.                              ULONG ulBufSize,
  406.                              PULONG pulSize)
  407. {
  408.     APIRET rc = _locatesysleveldata(hFile);
  409.     PSYSLEVELDATA psld = _allocsysleveldata(hFile);
  410.  
  411.     if (rc == 0)
  412.         rc = pBuffer ? rc : ERROR_INVALID_PARAMETER;
  413.     if (rc == 0)
  414.         rc = _readsysleveldata(hFile, psld);
  415.  
  416.     if (rc == 0)
  417.         switch (ulWhat)
  418.         {
  419.             case QLD_MAJORVERSION:
  420.                 if (ulBufSize >= 1)
  421.                 {
  422.                     psld->d_version[0] &= 0x0F;
  423.                     psld->d_version[0] |= (_achBuffer[0] - '0') << 4;
  424.                 }
  425.                 else
  426.                     rc = ERROR_INSUFFICIENT_BUFFER;
  427.                 _setsize(1);
  428.                 break;
  429.             case QLD_MINORVERSION:
  430.                 if (ulBufSize >= 2)
  431.                 {
  432.                     psld->d_version[0] &= 0xF0;
  433.                     psld->d_version[0] |= (_achBuffer[0] - '0') && 0x0F;
  434.                     psld->d_version[1] = _achBuffer[1] - '0';
  435.                 }
  436.                 else
  437.                     rc = ERROR_INSUFFICIENT_BUFFER;
  438.                 _setsize(2);
  439.                 break;
  440.             case QLD_REVISION:
  441.                 _setfield(d_revision);
  442.                 break;
  443.             case QLD_KIND:
  444.                 _setfield(d_kind);
  445.                 break;
  446.             case QLD_CURRENTCSD:
  447.                 memcpy(psld->d_plevel, psld->d_clevel, sizeof(psld->d_plevel));
  448.                 _setfield(d_clevel);
  449.                 break;
  450.             case QLD_TITLE:
  451.                 memcpy(psld->d_title, pBuffer, min(ulBufSize, sizeof(psld->d_title)));
  452.                 _setsize(min(ulBufSize, sizeof(psld->d_title)));
  453.                 break;
  454.             case QLD_ID:
  455.                 _setfield(d_cid);
  456.                 break;
  457.             case QLD_TYPE:
  458.                 break;
  459.             default:
  460.                 rc = ERROR_INVALID_PARAMETER;
  461.                 break;
  462.         }
  463.  
  464.     if (rc == 0)
  465.         rc = _locatesysleveldata(hFile);
  466.     if (rc == 0)
  467.         rc = _writesysleveldata(hFile, psld);
  468.  
  469.     _freesysleveldata(psld);
  470.  
  471.     return rc;
  472. }
  473.  
  474. /* ******************************************************************
  475.  *
  476.  *   Local helper functions
  477.  *
  478.  ********************************************************************/
  479.  
  480. /*
  481.  * _readsysleveldata:
  482.  *
  483.  */
  484.  
  485. static APIRET _readsysleveldata(HFILE hFile,
  486.                                 PSYSLEVELDATA psld)
  487. {
  488.     ULONG ulSize;
  489.  
  490.     return DosRead(hFile, psld, sizeof(SYSLEVELDATA), &ulSize);
  491. }
  492.  
  493. /*
  494.  * _writesysleveldata:
  495.  *
  496.  */
  497.  
  498. static APIRET _writesysleveldata(HFILE hFile,
  499.                                  PSYSLEVELDATA psld)
  500. {
  501.     ULONG ulSize;
  502.  
  503.     return DosWrite(hFile, psld, sizeof(SYSLEVELDATA), &ulSize);
  504. }
  505.  
  506. /*
  507.  * _locatesysleveldata:
  508.  *
  509.  */
  510.  
  511. static APIRET _locatesysleveldata(HFILE hFile)
  512. {
  513.     ULONG ulPos, ulData;
  514.     APIRET rc = DosSetFilePtr(hFile,
  515.                               FIELDOFFSET(SYSLEVELHEADER, h_data),
  516.                               FILE_BEGIN,
  517.                               &ulPos);
  518.  
  519.     rc = rc ? rc : DosRead(hFile, &ulData, sizeof(ulData), &ulPos);
  520.  
  521.     return rc ? rc : DosSetFilePtr(hFile, ulData, FILE_BEGIN, &ulPos);
  522. }
  523.  
  524. /*
  525.  * _allocsysleveldata:
  526.  *
  527.  */
  528.  
  529. static PSYSLEVELDATA _allocsysleveldata(HFILE hFile)
  530. {
  531.     return (PSYSLEVELDATA) malloc(sizeof(SYSLEVELDATA));
  532. }
  533.  
  534. /*
  535.  * _freesysleveldata:
  536.  *
  537.  */
  538.  
  539. static VOID _freesysleveldata(PSYSLEVELDATA psld)
  540. {
  541.     if (psld)
  542.         free(psld);
  543. }
  544.  
  545. /*
  546.  * _writesyslevelheader:
  547.  *
  548.  */
  549.  
  550. static APIRET _writesyslevelheader(HFILE hFile,
  551.                                    PSYSLEVELHEADER pslh)
  552. {
  553.     ULONG ulSize;
  554.  
  555.     return DosWrite(hFile, pslh, sizeof(SYSLEVELHEADER), &ulSize);
  556. }
  557.  
  558. /*
  559.  * _isasyslevelfile:
  560.  *
  561.  */
  562.  
  563. static BOOL _isasyslevelfile(PSZ pszFile,
  564.                              PSZ pszCID)
  565. {
  566.     HFILE hFile;
  567.     ULONG ulAction;
  568.     SYSLEVELHEADER slh;
  569.     APIRET rc = DosOpen(pszFile,
  570.                         &hFile,
  571.                         &ulAction,
  572.                         0,
  573.                         FILE_NORMAL,
  574.                         FILE_OPEN,
  575.                         OPEN_ACCESS_READONLY | OPEN_SHARE_DENYWRITE,
  576.                         0);
  577.  
  578.     if (rc == 0)
  579.         rc = DosRead(hFile, &slh, sizeof(slh), &ulAction);
  580.  
  581.     ulAction = (slh.h_magic[0] == 0xFF) && (slh.h_magic[1] == 0xFF);
  582.     if (ulAction)
  583.         ulAction = strcmp((PSZ)slh.h_name, "SYSLEVEL") == 0;
  584.     if (ulAction)
  585.         if (pszCID)
  586.         {
  587.             CHAR achCID[9];
  588.             ULONG ulCID;
  589.  
  590.             rc = lvlQueryLevelFileData(hFile, QLD_ID, achCID, 9, &ulCID);
  591.             ulAction = (strcmp(achCID, pszCID) == 0);
  592.         }
  593.  
  594.     rc = DosClose(hFile);
  595.  
  596.     return ulAction;
  597. }
  598.  
  599. /*
  600.  * _getfullname:
  601.  *
  602.  */
  603.  
  604. static APIRET _getfullname(PVOID pBuffer,
  605.                            PSZ pszFileName)
  606. {
  607.     ULONG ulDrive, ulDriveMap, ulBufSize = CCHMAXPATHCOMP;
  608.     APIRET rc = DosQueryCurrentDisk(&ulDrive, &ulDriveMap);
  609.  
  610.     _achBuffer[0] = '@' + ulDrive;
  611.     _achBuffer[1] = ':';
  612.     _achBuffer[2] = '\\';
  613.     rc = DosQueryCurrentDir(0, _achBuffer + 3, &ulBufSize);
  614.     strcat(strcat((PSZ)pBuffer, "\\"), pszFileName);
  615.  
  616.     return rc;
  617. }
  618.  
  619. /*
  620.  * _findsyslevelfile:
  621.  *
  622.  */
  623.  
  624. static APIRET _findsyslevelfile(PSZ pszName,
  625.                                 PSZ pszCID,
  626.                                 PVOID pBuffer)
  627. {
  628.     HDIR hDirHandle = HDIR_CREATE;
  629.     FILEFINDBUF3 ffb;
  630.     ULONG ulCount = 1, ulFound = 0;
  631.     CHAR achFileName[CCHMAXPATHCOMP] = "syslevel.";
  632.     APIRET rc = 0;
  633.  
  634.     if (pszName)
  635.         strcat(achFileName, pszName);
  636.     else
  637.         strcat(achFileName, "???");
  638.  
  639.     rc = DosFindFirst(achFileName,
  640.                       &hDirHandle,
  641.                       FILE_NORMAL,
  642.                       (PVOID) & ffb,
  643.                       sizeof(ffb),
  644.                       &ulCount,
  645.                       FIL_STANDARD);
  646.  
  647.     while ((!ulFound) && (rc == 0))
  648.     {
  649.         if (_isasyslevelfile(ffb.achName, pszCID))
  650.         {
  651.             _getfullname(pBuffer, ffb.achName);
  652.             ulFound = 1;
  653.             break;
  654.         }
  655.  
  656.         rc = DosFindNext(hDirHandle, (PVOID) & ffb, sizeof(ffb), &ulCount);
  657.     }
  658.  
  659.     if ((rc == 0) || (rc == 18))
  660.         rc = DosFindClose(hDirHandle);
  661.  
  662.     return ulFound ? 0 : 18;
  663. }
  664.  
  665. /*
  666.  * _findallsyslevelfiles:
  667.  *
  668.  */
  669.  
  670. static APIRET _findallsyslevelfiles(PSZ achFileName,
  671.                                     PSZ pszCID,
  672.                                     PPVOID ppBuffer,
  673.                                     PULONG pulBufSize)
  674. {
  675.     HDIR hDirHandle = HDIR_CREATE;
  676.     FILEFINDBUF3 ffb;
  677.     ULONG ulCount = 1, ulSize;
  678.     APIRET rc = DosFindFirst(achFileName,
  679.                              &hDirHandle,
  680.                              FILE_NORMAL,
  681.                              (PVOID) & ffb,
  682.                              sizeof(ffb),
  683.                              &ulCount,
  684.                              FIL_STANDARD);
  685.  
  686.     while (rc == 0)
  687.     {
  688.         if (_isasyslevelfile(ffb.achName, pszCID))
  689.         {
  690.             CHAR achBuf[CCHMAXPATHCOMP];
  691.  
  692.             _getfullname(achBuf, ffb.achName);
  693.             ulSize = strlen(achBuf);
  694.             if ((*pulBufSize) > ulSize)
  695.             {
  696.                 *pulBufSize -= ulSize + 1;
  697.                 memcpy(*ppBuffer, achBuf, ulSize);
  698.                 *ppBuffer = ((CHAR *) (*ppBuffer)) + ulSize + 1;
  699.             }
  700.             else
  701.                 rc = ERROR_INSUFFICIENT_BUFFER;
  702.         }
  703.  
  704.         if (rc == 0)
  705.             rc = DosFindNext(hDirHandle, (PVOID) & ffb, sizeof(ffb), &ulCount);
  706.     }
  707.  
  708.     if ((rc == 0) || (rc == 18))
  709.     {
  710.         rc = DosFindClose(hDirHandle);
  711.         if (rc == 6)
  712.             rc = 0;
  713.     }
  714.  
  715.     hDirHandle = HDIR_CREATE;
  716.     if (rc == 0)
  717.         rc = DosFindFirst("*",
  718.                           &hDirHandle,
  719.                           MUST_HAVE_DIRECTORY,
  720.                           (PVOID) & ffb,
  721.                           sizeof(ffb),
  722.                           &ulCount,
  723.                           FIL_STANDARD);
  724.  
  725.     while (rc == 0)
  726.     {
  727.         if (ffb.achName[0] == '.' &&
  728.             (ffb.achName[1] == 0 || (ffb.achName[1] == '.' && ffb.achName[2] == 0)))
  729.         {
  730.         }
  731.         else
  732.         {
  733.             rc = DosSetCurrentDir(ffb.achName);
  734.  
  735.             if (rc == 0)
  736.                 rc = _findallsyslevelfiles(achFileName, pszCID, ppBuffer, pulBufSize);
  737.  
  738.             if ((rc == 0) || (rc == 18))
  739.                 rc = DosSetCurrentDir("..");
  740.         }
  741.  
  742.         if (rc == 0)
  743.             rc = DosFindNext(hDirHandle, (PVOID) & ffb, sizeof(ffb), &ulCount);
  744.     }
  745.  
  746.     if ((rc == 0) || (rc == 18))
  747.     {
  748.         rc = DosFindClose(hDirHandle);
  749.         if (rc == 6)
  750.             rc = 0;
  751.     }
  752.  
  753.     return rc;
  754. }
  755.  
  756. /*
  757.  * _searchsubdirs:
  758.  *
  759.  */
  760.  
  761. static APIRET _searchsubdirs(PSZ pszName,
  762.                              PSZ pszCID,
  763.                              PVOID pBuffer)
  764. {
  765.     HDIR hDirHandle = HDIR_CREATE;
  766.     FILEFINDBUF3 ffb;
  767.     ULONG ulCount = 1, ulFound = 0;
  768.     APIRET rc = DosFindFirst("*",
  769.                              &hDirHandle,
  770.                              MUST_HAVE_DIRECTORY,
  771.                              (PVOID) & ffb,
  772.                              sizeof(ffb),
  773.                              &ulCount,
  774.                              FIL_STANDARD);
  775.  
  776.     while ((!ulFound) && (rc == 0))
  777.     {
  778.         if (ffb.achName[0] == '.' &&
  779.             (ffb.achName[1] == 0 || (ffb.achName[1] == '.' && ffb.achName[2] == 0)))
  780.         {
  781.         }
  782.         else
  783.         {
  784.             rc = DosSetCurrentDir(ffb.achName);
  785.  
  786.             if (rc == 0)
  787.                 rc = _findsyslevelfile(pszName, pszCID, pBuffer);
  788.  
  789.             if (rc == 18)
  790.                 rc = _searchsubdirs(pszName, pszCID, pBuffer);
  791.  
  792.             if (rc == 0)
  793.                 ulFound = 1;
  794.  
  795.             if ((rc == 0) || (rc == 18))
  796.                 rc = DosSetCurrentDir("..");
  797.         }
  798.  
  799.         if ((!ulFound) && (rc == 0))
  800.             rc = DosFindNext(hDirHandle, (PVOID) & ffb, sizeof(ffb), &ulCount);
  801.     }
  802.  
  803.     rc = DosFindClose(hDirHandle);
  804.  
  805.     return ulFound ? 0 : 18;
  806. }
  807.  
  808. /*
  809.  * _firstdrive:
  810.  *
  811.  */
  812.  
  813. static ULONG _firstdrive(ULONG ulDriveMap)
  814. {
  815.     return _nextdrive(ulDriveMap, 2);
  816. }
  817.  
  818. /*
  819.  * _nextdrive:
  820.  *
  821.  */
  822.  
  823. static ULONG _nextdrive(ULONG ulDriveMap,
  824.                         ULONG ulCurrent)
  825. {
  826.     ULONG ulNext = ulCurrent + 1;
  827.  
  828.     while (ulNext < 27)
  829.         if (ulDriveMap & (0x1 << (ulNext - 1)))
  830.             break;
  831.         else
  832.             ulNext++;
  833.  
  834.     if (ulNext < 27)
  835.     {
  836.         DosSetDefaultDisk(ulNext);
  837.         DosSetCurrentDir("\\");
  838.     }
  839.  
  840.     return (ulNext == 27) ? 0 : ulNext;
  841. }
  842.