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

  1.  
  2. /*
  3.  *@@sourcefile dosh2.c:
  4.  *      dosh.c contains more Control Program helper functions.
  5.  *
  6.  *      This file is new with V0.9.4 (2000-07-26) [umoeller].
  7.  *
  8.  *      As opposed to the functions in dosh.c, these require
  9.  *      linking against other helpers. As a result, these have
  10.  *      been separated from dosh.c to allow linking against
  11.  *      dosh.obj only.
  12.  *
  13.  *      Function prefixes:
  14.  *      --  dosh*   Dos (Control Program) helper functions
  15.  *
  16.  *      This has the same header as dosh.c, dosh.h.
  17.  *
  18.  *      The partition functions in this file are based on
  19.  *      code which has kindly been provided by Dmitry A. Steklenev.
  20.  *      See doshGetPartitionsList for how to use these.
  21.  *
  22.  *      Note: Version numbering in this file relates to XWorkplace version
  23.  *            numbering.
  24.  *
  25.  *@@header "helpers\dosh.h"
  26.  *@@added V0.9.4 (2000-07-27) [umoeller]
  27.  */
  28.  
  29. /*
  30.  *      This file Copyright (C) 1997-2000 Ulrich Möller,
  31.  *                                        Dmitry A. Steklenev.
  32.  *      This file is part of the "XWorkplace helpers" source package.
  33.  *      This is free software; you can redistribute it and/or modify
  34.  *      it under the terms of the GNU General Public License as published
  35.  *      by the Free Software Foundation, in version 2 as it comes in the
  36.  *      "COPYING" file of the XWorkplace main distribution.
  37.  *      This program is distributed in the hope that it will be useful,
  38.  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
  39.  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  40.  *      GNU General Public License for more details.
  41.  */
  42.  
  43. #define OS2EMX_PLAIN_CHAR
  44.     // this is needed for "os2emx.h"; if this is defined,
  45.     // emx will define PSZ as _signed_ char, otherwise
  46.     // as unsigned char
  47.  
  48. #define INCL_DOSMODULEMGR
  49. #define INCL_DOSPROCESS
  50. #define INCL_DOSSESMGR
  51. #define INCL_DOSQUEUES
  52. #define INCL_DOSMISC
  53. #define INCL_DOSDEVICES
  54. #define INCL_DOSDEVIOCTL
  55. #define INCL_DOSERRORS
  56. #include <os2.h>
  57.  
  58. #include <stdlib.h>
  59. #include <string.h>
  60. #include <stdio.h>
  61. #include <ctype.h>
  62.  
  63. #include "setup.h"                      // code generation and debugging options
  64.  
  65. #include "helpers\dosh.h"
  66. #include "helpers\ensure.h"
  67. #include "helpers\nls.h"
  68. #include "helpers\standards.h"
  69. #include "helpers\stringh.h"
  70.  
  71. #pragma hdrstop
  72.  
  73. /*
  74.  *@@category: Helpers\Control program helpers\Miscellaneous
  75.  */
  76.  
  77. /* ******************************************************************
  78.  *
  79.  *   Miscellaneous
  80.  *
  81.  ********************************************************************/
  82.  
  83. /*
  84.  *@@ doshIsValidFileName:
  85.  *      this returns NO_ERROR only if pszFile is a valid file name.
  86.  *      This may include a full path.
  87.  *
  88.  *      If a drive letter is specified, this checks for whether
  89.  *      that drive is a FAT drive and adjust the checks accordingly,
  90.  *      i.e. 8+3 syntax (per path component).
  91.  *
  92.  *      If no drive letter is specified, this check is performed
  93.  *      for the current drive.
  94.  *
  95.  *      This also checks if pszFileNames contains characters which
  96.  *      are invalid for the current drive.
  97.  *
  98.  *      Note: this performs syntactic checks only. This does not
  99.  *      check for whether the specified path components exist.
  100.  *      However, it _is_ checked for whether the given drive
  101.  *      exists.
  102.  *
  103.  *      This func is especially useful to check filenames that
  104.  *      have been entered by the user in a "Save as" dialog.
  105.  *
  106.  *      If an error is found, the corresponding DOS error code
  107.  *      is returned:
  108.  *      --  ERROR_INVALID_DRIVE
  109.  *      --  ERROR_FILENAME_EXCED_RANGE  (on FAT: no 8+3 filename)
  110.  *      --  ERROR_INVALID_NAME          (invalid character)
  111.  *      --  ERROR_CURRENT_DIRECTORY     (if fFullyQualified: no full path specified)
  112.  *
  113.  *@@changed V0.9.2 (2000-03-11) [umoeller]: added fFullyQualified
  114.  */
  115.  
  116. APIRET doshIsValidFileName(const char* pcszFile,
  117.                            BOOL fFullyQualified)    // in: if TRUE, pcszFile must be fully q'fied
  118. {
  119.     APIRET  arc = NO_ERROR;
  120.     CHAR    szPath[CCHMAXPATH+4] = " :";
  121.     CHAR    szComponent[CCHMAXPATH];
  122.     PSZ     p1, p2;
  123.     BOOL    fIsFAT = FALSE;
  124.     PSZ     pszInvalid;
  125.  
  126.     if (fFullyQualified)    // V0.9.2 (2000-03-11) [umoeller]
  127.     {
  128.         if (    (*(pcszFile + 1) != ':')
  129.              || (*(pcszFile + 2) != '\\')
  130.            )
  131.             arc = ERROR_CURRENT_DIRECTORY;
  132.     }
  133.  
  134.     // check drive first
  135.     if (*(pcszFile + 1) == ':')
  136.     {
  137.         CHAR cDrive = toupper(*pcszFile);
  138.         double d;
  139.         // drive specified:
  140.         strcpy(szPath, pcszFile);
  141.         szPath[0] = toupper(*pcszFile);
  142.         arc = doshQueryDiskFree(cDrive - 'A' + 1, &d);
  143.     }
  144.     else
  145.     {
  146.         // no drive specified: take current
  147.         ULONG   ulDriveNum = 0,
  148.                 ulDriveMap = 0;
  149.         arc = DosQueryCurrentDisk(&ulDriveNum, &ulDriveMap);
  150.         szPath[0] = ((UCHAR)ulDriveNum) + 'A' - 1;
  151.         szPath[1] = ':';
  152.         strcpy(&szPath[2], pcszFile);
  153.     }
  154.  
  155.     if (arc == NO_ERROR)
  156.     {
  157.         fIsFAT = doshIsFileOnFAT(szPath);
  158.  
  159.         pszInvalid = (fIsFAT)
  160.                         ? "<>|+=:;,\"/[] "  // invalid characters in FAT
  161.                         : "<>|:\"/";        // invalid characters in IFS's
  162.  
  163.         // now separate path components
  164.         p1 = &szPath[2];       // advance past ':'
  165.  
  166.         do {
  167.  
  168.             if (*p1 == '\\')
  169.                 p1++;
  170.  
  171.             p2 = strchr(p1, '\\');
  172.             if (p2 == NULL)
  173.                 p2 = p1 + strlen(p1);
  174.  
  175.             if (p1 != p2)
  176.             {
  177.                 LONG    lDotOfs = -1,
  178.                         lAfterDot = -1;
  179.                 ULONG   cbFile,
  180.                         ul;
  181.                 PSZ     pSource = szComponent;
  182.  
  183.                 strncpy(szComponent, p1, p2-p1);
  184.                 szComponent[p2-p1] = 0;
  185.                 cbFile = strlen(szComponent);
  186.  
  187.                 // now check each path component
  188.                 for (ul = 0; ul < cbFile; ul++)
  189.                 {
  190.                     if (fIsFAT)
  191.                     {
  192.                         // on FAT: only 8 characters allowed before dot
  193.                         if (*pSource == '.')
  194.                         {
  195.                             lDotOfs = ul;
  196.                             lAfterDot = 0;
  197.                             if (ul > 7)
  198.                                 return (ERROR_FILENAME_EXCED_RANGE);
  199.                         }
  200.                     }
  201.                     // and check for invalid characters
  202.                     if (strchr(pszInvalid, *pSource) != NULL)
  203.                         return (ERROR_INVALID_NAME);
  204.  
  205.                     pSource++;
  206.  
  207.                     // on FAT, allow only three chars after dot
  208.                     if (fIsFAT)
  209.                         if (lAfterDot != -1)
  210.                         {
  211.                             lAfterDot++;
  212.                             if (lAfterDot > 3)
  213.                                 return (ERROR_FILENAME_EXCED_RANGE);
  214.                         }
  215.                 }
  216.  
  217.                 // we are still missing the case of a FAT file
  218.                 // name without extension; if so, check whether
  219.                 // the file stem is <= 8 chars
  220.                 if (fIsFAT)
  221.                     if (lDotOfs == -1)  // dot not found:
  222.                         if (cbFile > 8)
  223.                             return (ERROR_FILENAME_EXCED_RANGE);
  224.             }
  225.  
  226.             // go for next component
  227.             p1 = p2+1;
  228.         } while (*p2);
  229.     }
  230.  
  231.     return arc;
  232. }
  233.  
  234. /*
  235.  *@@ doshMakeRealName:
  236.  *      this copies pszSource to pszTarget, replacing
  237.  *      all characters which are not supported by file
  238.  *      systems with cReplace.
  239.  *
  240.  *      pszTarget must be at least the same size as pszSource.
  241.  *      If (fIsFAT), the file name will be made FAT-compliant (8+3).
  242.  *
  243.  *      Returns TRUE if characters were replaced.
  244.  *
  245.  *@@changed V0.9.0 (99-11-06) [umoeller]: now replacing "*" too
  246.  */
  247.  
  248. BOOL doshMakeRealName(PSZ pszTarget,    // out: new real name
  249.                       PSZ pszSource,    // in: filename to translate
  250.                       CHAR cReplace,    // in: replacement char for invalid
  251.                                         //     characters (e.g. '!')
  252.                       BOOL fIsFAT)      // in: make-FAT-compatible flag
  253. {
  254.     ULONG ul,
  255.           cbSource = strlen(pszSource);
  256.     LONG  lDotOfs = -1,
  257.           lAfterDot = -1;
  258.     BOOL  brc = FALSE;
  259.     PSZ   pSource = pszSource,
  260.           pTarget = pszTarget;
  261.  
  262.     const char *pcszInvalid = (fIsFAT)
  263.                                    ? "*<>|+=:;,\"/\\[] "  // invalid characters in FAT
  264.                                    : "*<>|:\"/\\"; // invalid characters in IFS's
  265.  
  266.     for (ul = 0; ul < cbSource; ul++)
  267.     {
  268.         if (fIsFAT)
  269.         {
  270.             // on FAT: truncate filename if neccessary
  271.             if (*pSource == '.')
  272.             {
  273.                 lDotOfs = ul;
  274.                 lAfterDot = 0;
  275.                 if (ul > 7) {
  276.                     // only 8 characters allowed before dot,
  277.                     // so set target ptr to dot pos
  278.                     pTarget = pszTarget+8;
  279.                 }
  280.             }
  281.         }
  282.         // and replace invalid characters
  283.         if (strchr(pcszInvalid, *pSource) == NULL)
  284.             *pTarget = *pSource;
  285.         else
  286.         {
  287.             *pTarget = cReplace;
  288.             brc = TRUE;
  289.         }
  290.         pTarget++;
  291.         pSource++;
  292.  
  293.         // on FAT, allow only three chars after dot
  294.         if (fIsFAT)
  295.             if (lAfterDot != -1)
  296.             {
  297.                 lAfterDot++;
  298.                 if (lAfterDot > 3)
  299.                     break;
  300.             }
  301.     }
  302.     *pTarget = '\0';
  303.  
  304.     if (fIsFAT)
  305.     {
  306.         // we are still missing the case of a FAT file
  307.         // name without extension; if so, check whether
  308.         // the file stem is <= 8 chars
  309.         if (lDotOfs == -1)  // dot not found:
  310.             if (cbSource > 8)
  311.                 *(pszTarget+8) = 0; // truncate
  312.  
  313.         // convert to upper case
  314.         strupr(pszTarget);
  315.     }
  316.  
  317.     return brc;
  318. }
  319.  
  320. /*
  321.  *@@ doshSetCurrentDir:
  322.  *      sets the current working directory
  323.  *      to the given path.
  324.  *
  325.  *      As opposed to DosSetCurrentDir, this
  326.  *      one will change the current drive
  327.  *      also, if one is specified.
  328.  *
  329.  *@@changed V0.9.9 (2001-04-04) [umoeller]: this returned an error even if none occured, fixed
  330.  */
  331.  
  332. APIRET doshSetCurrentDir(const char *pcszDir)
  333. {
  334.     APIRET  arc = NO_ERROR;
  335.     if (!pcszDir)
  336.         return (ERROR_INVALID_PARAMETER);
  337.     {
  338.         if (*pcszDir != 0)
  339.             if (*(pcszDir+1) == ':')
  340.             {
  341.                 // drive given:
  342.                 CHAR    cDrive = toupper(*(pcszDir));
  343.                 // change drive
  344.                 arc = DosSetDefaultDisk( (ULONG)(cDrive - 'A' + 1) );
  345.                         // 1 = A:, 2 = B:, ...
  346.             }
  347.  
  348.         arc = DosSetCurrentDir((PSZ)pcszDir);
  349.     }
  350.  
  351.     return arc;       // V0.9.9 (2001-04-04) [umoeller]
  352. }
  353.  
  354. /*
  355.  *@@ CopyToBuffer:
  356.  *      little helper for copying a string to
  357.  *      a target buffer with length checking.
  358.  *
  359.  *      Returns:
  360.  *
  361.  *      --  NO_ERROR
  362.  *
  363.  *      --  ERROR_BUFFER_OVERFLOW if pszTarget does
  364.  *          not have enough room to hold pcszSource
  365.  *          (including the null terminator).
  366.  *
  367.  *@@added V0.9.16 (2001-10-08) [umoeller]
  368.  */
  369.  
  370. static APIRET CopyToBuffer(PSZ pszTarget,      // out: target buffer
  371.                            PCSZ pcszSource,    // in: source string
  372.                            ULONG cbTarget)     // in: size of target buffer
  373. {
  374.     ULONG ulLength = strlen(pcszSource);
  375.     if (ulLength < cbTarget)
  376.     {
  377.         memcpy(pszTarget,
  378.                pcszSource,
  379.                ulLength + 1);
  380.         return NO_ERROR;
  381.     }
  382.  
  383.     return(ERROR_BUFFER_OVERFLOW);
  384. }
  385.  
  386. /*
  387.  *@@ doshSearchPath:
  388.  *      replacement for DosSearchPath.
  389.  *
  390.  *      This looks along all directories which are
  391.  *      specified in the value of the given environment
  392.  *      variable if pcszFile is found.
  393.  *
  394.  *      As opposed to the stupid DosSearchPath, this
  395.  *      ignores subdirectories in the path particles.
  396.  *      For example, DosSearchPath would usually not
  397.  *      find an INSTALL file because \OS2 contains
  398.  *      an INSTALL directory, or NETSCAPE because
  399.  *      \OS2\INSTALL contains a NETSCAPE directory.
  400.  *
  401.  *      Returns:
  402.  *
  403.  *      --  NO_ERROR: pszExecutable has received the
  404.  *          full path of pcszFile.
  405.  *
  406.  *      --  ERROR_FILE_NOT_FOUND: pcszFile was not found
  407.  *          in the specified path (or is a directory).
  408.  *
  409.  *      --  ERROR_BUFFER_OVERFLOW: pcszFile was found, but
  410.  *          the pszExecutable buffer is too small to hold
  411.  *          the full path.
  412.  *
  413.  *@@added V0.9.16 (2001-10-08) [umoeller]
  414.  */
  415.  
  416. APIRET doshSearchPath(const char *pcszPath,     // in: path variable name (e.g. "PATH")
  417.                       const char *pcszFile,     // in: file to look for (e.g. "LVM.EXE")
  418.                       PSZ pszExecutable,        // out: full path (e.g. "F:\os2\lvm.exe")
  419.                       ULONG cbExecutable)       // in: sizeof (*pszExecutable)
  420. {
  421.     APIRET arc = NO_ERROR;
  422.  
  423.     // get the PATH value
  424.     PCSZ pcszPathValue;
  425.     if (!(arc = DosScanEnv((PSZ)pcszPath,
  426. #if __cplusplus
  427.                            &pcszPathValue)))
  428. #else
  429.                            (PSZ*)&pcszPathValue)))
  430. #endif
  431.     {
  432.         // run thru the path components
  433.         PSZ pszPathCopy;
  434.         if (pszPathCopy = strdup(pcszPathValue))
  435.         {
  436.             PSZ pszToken = strtok(pszPathCopy, ";");
  437.             while (pszToken)
  438.             {
  439.                 CHAR szFileMask[2*CCHMAXPATH];
  440.                 FILESTATUS3 fs3;
  441.  
  442.                 sprintf(szFileMask,
  443.                         "%s\\%s",
  444.                         pszToken,           // path particle
  445.                         pcszFile);          // e.g. "netscape"
  446.  
  447.                 if (    (!(arc = DosQueryPathInfo(szFileMask,
  448.                                                   FIL_STANDARD,
  449.                                                   &fs3,
  450.                                                   sizeof(fs3))))
  451.                      // make sure it's not a directory
  452.                      // and that it's not hidden
  453.                      && (!(fs3.attrFile & (FILE_DIRECTORY | FILE_HIDDEN)))
  454.                    )
  455.                 {
  456.                     // copy
  457.                     arc = CopyToBuffer(pszExecutable,
  458.                                        szFileMask,
  459.                                        cbExecutable);
  460.                     // and stop
  461.                     break;
  462.                 }
  463.                 else
  464.                     arc = ERROR_FILE_NOT_FOUND;
  465.                     // and search on
  466.  
  467.                 pszToken = strtok(NULL, ";");
  468.             };
  469.  
  470.             free(pszPathCopy);
  471.         }
  472.         else
  473.             arc = ERROR_NOT_ENOUGH_MEMORY;
  474.     }
  475.  
  476.     return arc;
  477. }
  478.  
  479. /*
  480.  * FindFile:
  481.  *      helper for doshFindExecutable.
  482.  *
  483.  *added V0.9.11 (2001-04-25) [umoeller]
  484.  *@@changed V0.9.16 (2001-10-08) [umoeller]: rewrote second half for DosSearchPath replacement, which returns directories too
  485.  */
  486.  
  487. static APIRET FindFile(const char *pcszCommand,      // in: command (e.g. "lvm")
  488.                        PSZ pszExecutable,            // out: full path (e.g. "F:\os2\lvm.exe")
  489.                        ULONG cbExecutable)           // in: sizeof (*pszExecutable)
  490. {
  491.     APIRET arc = NO_ERROR;
  492.     FILESTATUS3 fs3;
  493.  
  494.     if (    (strchr(pcszCommand, '\\'))
  495.          || (strchr(pcszCommand, ':'))
  496.        )
  497.     {
  498.         // looks like this is qualified:
  499.         arc = DosQueryPathInfo((PSZ)pcszCommand,
  500.                                FIL_STANDARD,
  501.                                &fs3,
  502.                                sizeof(fs3));
  503.         if (!arc)
  504.             if (!(fs3.attrFile & FILE_DIRECTORY))
  505.                 arc = CopyToBuffer(pszExecutable,
  506.                                    pcszCommand,
  507.                                    cbExecutable);
  508.             else
  509.                 // directory:
  510.                 arc = ERROR_INVALID_EXE_SIGNATURE;
  511.     }
  512.     else
  513.     {
  514.         // non-qualified:
  515.         /* arc = DosSearchPath(SEARCH_IGNORENETERRS
  516.                                 | SEARCH_ENVIRONMENT
  517.                                 | SEARCH_CUR_DIRECTORY,
  518.                             "PATH",
  519.                             (PSZ)pcszCommand,
  520.                             pszExecutable,
  521.                             cbExecutable); */
  522.             // The above is not useable. It returns directories
  523.             // on the path... for example, it returns \OS2\INSTALL\NETSCAPE
  524.             // if netscape is looked for. So we search manually... sigh.
  525.             // V0.9.16 (2001-10-08) [umoeller]
  526.         arc = doshSearchPath("PATH",
  527.                              pcszCommand,
  528.                              pszExecutable,
  529.                              cbExecutable);
  530.     }
  531.  
  532.     return arc;
  533. }
  534.  
  535. /*
  536.  *@@ doshFindExecutable:
  537.  *      this attempts to find an executable by doing the
  538.  *      following:
  539.  *
  540.  *      1)  If pcszCommand appears to be qualified (i.e. contains
  541.  *          a backslash), this checks for whether the file exists.
  542.  *          If it is a directory, ERROR_INVALID_EXE_SIGNATURE is
  543.  *          returned.
  544.  *
  545.  *      2)  If pcszCommand contains no backslash, this searches
  546.  *          all directories on the PATH in order to find the full
  547.  *          path of the executable. Starting with V0.9.16, we
  548.  *          use doshSearchPath for that.
  549.  *
  550.  *      papcszExtensions determines if additional searches are to be
  551.  *      performed if the file doesn't exist (case 1) or doshSearchPath
  552.  *      returned ERROR_FILE_NOT_FOUND (case 2).
  553.  *      This must point to an array of strings specifying the extra
  554.  *      extensions to search for.
  555.  *
  556.  *      If both papcszExtensions and cExtensions are null, no
  557.  *      extra searches are performed.
  558.  *
  559.  *      Returns:
  560.  *
  561.  *      --  NO_ERROR: pszExecutable has received the full path of
  562.  *          the executable found by DosSearchPath.
  563.  *
  564.  *      --  ERROR_FILE_NOT_FOUND
  565.  *
  566.  *      --  ERROR_BUFFER_OVERFLOW: pcszCommand was found, but
  567.  *          the pszExecutable buffer is too small to hold
  568.  *          the full path.
  569.  *
  570.  *      Example:
  571.  *
  572.  +      const char *aExtensions[] = {  "EXE",
  573.  +                                     "COM",
  574.  +                                     "CMD"
  575.  +                                  };
  576.  +      CHAR szExecutable[CCHMAXPATH];
  577.  +      APIRET arc = doshFindExecutable("lvm",
  578.  +                                      szExecutable,
  579.  +                                      sizeof(szExecutable),
  580.  +                                      aExtensions,
  581.  +                                      3);
  582.  *
  583.  *@@added V0.9.9 (2001-03-07) [umoeller]
  584.  *@@changed V0.9.11 (2001-04-25) [umoeller]: this never worked for qualified pcszCommand's, fixed
  585.  */
  586.  
  587. APIRET doshFindExecutable(const char *pcszCommand,      // in: command (e.g. "lvm")
  588.                           PSZ pszExecutable,            // out: full path (e.g. "F:\os2\lvm.exe")
  589.                           ULONG cbExecutable,           // in: sizeof (*pszExecutable)
  590.                           const char **papcszExtensions, // in: array of extensions (without dots)
  591.                           ULONG cExtensions)            // in: array item count
  592. {
  593.     APIRET arc = FindFile(pcszCommand,
  594.                           pszExecutable,
  595.                           cbExecutable);
  596.  
  597.     if (    (arc == ERROR_FILE_NOT_FOUND)           // not found?
  598.          && (cExtensions)                    // any extra searches wanted?
  599.        )
  600.     {
  601.         // try additional things then
  602.         PSZ psz2 = (PSZ)malloc(strlen(pcszCommand) + 20);
  603.         if (psz2)
  604.         {
  605.             ULONG   ul;
  606.             for (ul = 0;
  607.                  ul < cExtensions;
  608.                  ul++)
  609.             {
  610.                 const char *pcszExtThis = papcszExtensions[ul];
  611.                 sprintf(psz2,
  612.                         "%s.%s",
  613.                         pcszCommand,
  614.                         pcszExtThis);
  615.                 arc = FindFile(psz2,
  616.                                pszExecutable,
  617.                                cbExecutable);
  618.                 if (arc != ERROR_FILE_NOT_FOUND)
  619.                     break;
  620.             }
  621.  
  622.             free(psz2);
  623.         }
  624.         else
  625.             arc = ERROR_NOT_ENOUGH_MEMORY;
  626.     }
  627.  
  628.     return arc;
  629. }
  630.  
  631. /*
  632.  *@@category: Helpers\Control program helpers\Partitions info
  633.  *      functions for retrieving partition information directly
  634.  *      from the partition tables on the disk. See doshGetPartitionsList.
  635.  */
  636.  
  637. /********************************************************************
  638.  *
  639.  *   Partition functions
  640.  *
  641.  ********************************************************************/
  642.  
  643. /*
  644.  *@@ doshQueryDiskCount:
  645.  *      returns the no. of physical disks installed
  646.  *      on the system.
  647.  *
  648.  *@@added V0.9.0 [umoeller]
  649.  */
  650.  
  651. UINT doshQueryDiskCount(VOID)
  652. {
  653.     USHORT usCount = 0;
  654.     DosPhysicalDisk(INFO_COUNT_PARTITIONABLE_DISKS, &usCount, 2, 0, 0);
  655.     return (usCount);
  656. }
  657.  
  658. /*
  659.  *@@ doshType2FSName:
  660.  *      this returns a static, zero-terminated string
  661.  *      for the given FS type, or NULL if the type
  662.  *      is unknown.
  663.  *
  664.  *@@added V0.9.0 [umoeller]
  665.  *@@changed V0.9.16 (2001-10-08) [umoeller]: rewritten
  666.  */
  667.  
  668. const char* doshType2FSName(unsigned char bFSType)  // in: FS type
  669. {
  670.     switch (bFSType)
  671.     {
  672.         case 0x00: return "empty";
  673.         case 0x01: return "DOS 12-bit FAT < 10 Mb";
  674.         case 0x02: return "XENIX root file system";
  675.         case 0x03: return "XENIX /usr file system (obsolete)";
  676.         case 0x04: return "DOS 16-bit FAT < 32 Mb";
  677.         case 0x05: return "DOS 3.3+ extended partition";
  678.         case 0x06: return "DOS 3.31+ 16-bit FAT > 32 Mb";
  679.         case 0x07: return "HPFS/NTFS/QNX/Advanced Unix";
  680.         case 0x08: return "OS/2 1.0-1.3/AIX/Commodore/DELL";
  681.         case 0x09: return "AIX data/Coherent";
  682.         case 0x0A: return "OS/2 Boot Manager/OPUS/Coherent Swap";
  683.         case 0x0B: return "Windows95 with 32-bit FAT";
  684.         case 0x0C: return "Windows95 with 32-bit FAT (LBA)";
  685.         case 0x0E: return "Windows 95 VFAT (06h plus LBA)";
  686.         case 0x0F: return "Windows 95 VFAT (05h plus LBA)";
  687.         case 0x10: return "OPUS";
  688.         case 0x11: return "OS/2 Boot Manager hidden 12-bit FAT";
  689.         case 0x12: return "Compaq Diagnostics";
  690.         case 0x14: return "OS/2 Boot Manager hidden sub-32M 16-bit FAT";
  691.         case 0x16: return "OS/2 Boot Manager hidden over-32M 16-bit FAT";
  692.         case 0x17: return "OS/2 Boot Manager hidden HPFS";
  693.         case 0x18: return "AST special Windows swap file (\"Zero-Volt Suspend\")";
  694.         // case 0x21: reserved
  695.         // case 0x23: reserved
  696.         case 0x24: return "NEC MS-DOS 3.x";
  697.         // case 0x26: reserved
  698.         // case 0x31: reserved
  699.         // case 0x33: reserved
  700.         // case 0x34: reserved
  701.         // case 0x36: reserved
  702.         case 0x38: return "Theos";
  703.         case 0x3C: return "PowerQuest PartitionMagic recovery partition";
  704.         case 0x40: return "VENIX 80286";
  705.         case 0x41: return "Personal RISC Boot";
  706.         case 0x42: return "SFS (Secure File System) by Peter Gutmann";
  707.         case 0x50: return "OnTrack Disk Manager, read-only";
  708.         case 0x51: return "OnTrack Disk Manager, read/write";
  709.         case 0x52: return "CP/M or Microport System V/386";
  710.         case 0x53: return "OnTrack Disk Manager, write-only???";
  711.         case 0x54: return "OnTrack Disk Manager (DDO)";
  712.         case 0x56: return "GoldenBow VFeature";
  713.         case 0x61: return "SpeedStor";
  714.         case 0x63: return "Unix SysV/386, 386/ix or Mach, MtXinu BSD 4.3 on Mach or GNU HURD";
  715.         case 0x64: return "Novell NetWare 286";
  716.         case 0x65: return "Novell NetWare (3.11)";
  717.         case 0x67:
  718.         case 0x68:
  719.         case 0x69: return "Novell";
  720.         case 0x70: return "DiskSecure Multi-Boot";
  721.         // case 0x71: reserved
  722.         // case 0x73: reserved
  723.         // case 0x74: reserved
  724.         case 0x75: return "PC/IX";
  725.         // case 0x76: reserved
  726.         case 0x80: return "Minix v1.1 - 1.4a";
  727.         case 0x81: return "Minix v1.4b+ or Linux or Mitac Advanced Disk Manager";
  728.         case 0x82: return "Linux Swap or Prime";
  729.         case 0x83: return "Linux native file system (ext2fs/xiafs)";
  730.         case 0x84: return "OS/2-renumbered type 04h (hidden DOS C: drive)";
  731.         case 0x86: return "FAT16 volume/stripe set (Windows NT)";
  732.         case 0x87: return "HPFS Fault-Tolerant mirrored partition or NTFS volume/stripe set";
  733.         case 0x93: return "Amoeba file system";
  734.         case 0x94: return "Amoeba bad block table";
  735.         case 0xA0: return "Phoenix NoteBIOS Power Management \"Save-to-Disk\" partition";
  736.         // case 0xA1: reserved
  737.         // case 0xA3: reserved
  738.         // case 0xA4: reserved
  739.         case 0xA5: return "FreeBSD, BSD/386";
  740.         // case 0xA6: reserved
  741.         // case 0xB1: reserved
  742.         // case 0xB3: reserved
  743.         // case 0xB4: reserved
  744.         // case 0xB6: reserved
  745.         case 0xB7: return "BSDI file system (secondarily swap)";
  746.         case 0xB8: return "BSDI swap (secondarily file system)";
  747.         case 0xC1: return "DR DOS 6.0 LOGIN.EXE-secured 12-bit FAT";
  748.         case 0xC4: return "DR DOS 6.0 LOGIN.EXE-secured 16-bit FAT";
  749.         case 0xC6: return "DR DOS 6.0 LOGIN.EXE-secured Huge partition or NT corrupted FAT16 volume/stripe set";
  750.         case 0xC7: return "Syrinx Boot or corrupted NTFS volume/stripe set";
  751.         case 0xD8: return "CP/M-86";
  752.         case 0xDB: return "CP/M, Concurrent CP/M, Concurrent DOS, Convergent Technologies OS";
  753.         case 0xE1: return "SpeedStor 12-bit FAT extended partition";
  754.         case 0xE3: return "DOS read-only or Storage Dimensions";
  755.         case 0xE4: return "SpeedStor 16-bit FAT extended partition";
  756.         // case 0xE5: reserved
  757.         // case 0xE6: reserved
  758.         case 0xF1: return "Storage Dimensions";
  759.         case 0xF2: return "DOS 3.3+ secondary partition";
  760.         // case 0xF3: reserved
  761.         case 0xF4: return "SpeedStor or Storage Dimensions";
  762.         // case 0xF6: reserved
  763.         case 0xFE: return "LANstep or IBM PS/2 IML";
  764.         case 0xFF: return "Xenix bad block table";
  765.     }
  766.  
  767.     return NULL;
  768. }
  769.  
  770. /*
  771.  * AppendPartition:
  772.  *      this appends the given partition information to
  773.  *      the given partition list. To do this, a new
  774.  *      PARTITIONINFO structure is created and appended
  775.  *      in a list (managed thru the PARTITIONINFO.pNext
  776.  *      items).
  777.  *
  778.  *      pppiThis must be a pointer to a pointer to a PARTITIONINFO.
  779.  *      With each call of this function, this pointer is advanced
  780.  *      to point to the newly created PARTITIONINFO, so before
  781.  *      calling this function for the first time,
  782.  *
  783.  *@@added V0.9.0 [umoeller]
  784.  *@@changed V0.9.20 (2002-08-10) [umoeller]: fixed truncated LVM names
  785.  */
  786.  
  787. static APIRET AppendPartition(PARTITIONINFO **pppiFirst,
  788.                               PARTITIONINFO **pppiThis,    // in/out: partition info; pointer will be advanced
  789.                               PUSHORT posCount,            // in/out: partition count
  790.                               BYTE bDisk,                  // in: disk of partition
  791.                               const char *pszBootName,     // in: boot partition name
  792.                               CHAR cLetter,                // in/out: drive letter
  793.                               BYTE bFsType,                // in: file system type
  794.                               BOOL fPrimary,               // in: primary?
  795.                               BOOL fBootable,
  796.                               ULONG ulSectors)             // in: no. of sectors
  797. {
  798.     APIRET arc = NO_ERROR;
  799.     PPARTITIONINFO ppiNew;
  800.     if (ppiNew = NEW(PARTITIONINFO))
  801.     {
  802.         ZERO(ppiNew);
  803.  
  804.         // store data
  805.         ppiNew->bDisk = bDisk;
  806.         if (    (fBootable)
  807.              && (pszBootName)
  808.            )
  809.         {
  810.             // fixed truncated LVM names V0.9.20 (2002-08-10) [umoeller]
  811.             strhncpy0(ppiNew->szBootName,
  812.                       pszBootName,
  813.                       sizeof(ppiNew->szBootName));
  814.         }
  815.         else
  816.             ppiNew->szBootName[0] = 0;
  817.         ppiNew->cLetter = cLetter;
  818.         ppiNew->bFSType = bFsType;
  819.         ppiNew->pcszFSType = doshType2FSName(bFsType);
  820.         ppiNew->fPrimary = fPrimary;
  821.         ppiNew->fBootable = fBootable;
  822.         ppiNew->ulSize = ulSectors / 2048;
  823.  
  824.         ppiNew->pNext = NULL;
  825.  
  826.         (*posCount)++;
  827.  
  828.         if (*pppiFirst == (PPARTITIONINFO)NULL)
  829.         {
  830.             // first call:
  831.             *pppiFirst = ppiNew;
  832.             *pppiThis = ppiNew;
  833.         }
  834.         else
  835.         {
  836.             // append to list
  837.             (**pppiThis).pNext = ppiNew;
  838.             *pppiThis = ppiNew;
  839.         }
  840.     }
  841.     else
  842.         arc = ERROR_NOT_ENOUGH_MEMORY;
  843.  
  844.     return arc;
  845. }
  846.  
  847. #ifndef __XWPLITE__
  848.  
  849. /*
  850.  *@@ doshReadSector:
  851.  *      reads a physical disk sector.
  852.  *
  853.  *      If NO_ERROR is returned, the sector contents
  854.  *      have been stored in *buff.
  855.  *
  856.  *      Originally contributed by Dmitry A. Steklenev.
  857.  *
  858.  *@@added V0.9.0 [umoeller]
  859.  *@@changed V0.9.9 (2001-04-04) [umoeller]: added more error checking
  860.  */
  861.  
  862. APIRET doshReadSector(USHORT disk,      // in: physical disk no. (1, 2, 3, ...)
  863.                       void *buff,
  864.                       USHORT head,
  865.                       USHORT cylinder,
  866.                       USHORT sector)
  867. {
  868.     APIRET  arc;
  869.     HFILE   dh = 0;
  870.     char    dn[256];
  871.  
  872.     sprintf(dn, "%u:", disk);
  873.     if (!(arc = DosPhysicalDisk(INFO_GETIOCTLHANDLE, &dh, 2, dn, 3)))
  874.     {
  875.         TRACKLAYOUT DiskIOParm;
  876.         ULONG IOCtlDataLength = sizeof(DiskIOParm);
  877.         ULONG IOCtlParmLength = 512;
  878.  
  879.         DiskIOParm.bCommand = 0;
  880.         DiskIOParm.usHead = head;
  881.         DiskIOParm.usCylinder = cylinder;
  882.         DiskIOParm.usFirstSector = 0;
  883.         DiskIOParm.cSectors = 1;
  884.         DiskIOParm.TrackTable[0].usSectorNumber = sector;
  885.         DiskIOParm.TrackTable[0].usSectorSize = 512;
  886.  
  887.         arc = DosDevIOCtl(dh,
  888.                           IOCTL_PHYSICALDISK, PDSK_READPHYSTRACK,
  889.                           &DiskIOParm, IOCtlParmLength, &IOCtlParmLength,
  890.                           buff       , IOCtlDataLength, &IOCtlDataLength);
  891.  
  892.         DosPhysicalDisk(INFO_FREEIOCTLHANDLE, 0, 0, &dh, 2);
  893.     }
  894.  
  895.     return arc;
  896. }
  897.  
  898. // Sector and Cylinder values are actually 6 bits and 10 bits:
  899. //
  900. //   1 1 1 1 1 1
  901. //  ┌5┬4┬3┬2┬1┬0┬9┬8┬7┬6┬5┬4┬3┬2┬1┬─┐
  902. //  │c c c c c c c c C c S s s s s s│
  903. //  └─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┘
  904. //
  905. // The high two bits of the second byte are used as the high bits
  906. // of a 10-bit value.  This allows for as many as 1024 cylinders
  907. // and 64 sectors per cylinder.
  908.  
  909. /*
  910.  * GetCyl:
  911.  *      get cylinder number.
  912.  *
  913.  *      Originally contributed by Dmitry A. Steklenev.
  914.  *
  915.  *@@added V0.9.0 [umoeller]
  916.  */
  917.  
  918. static USHORT GetCyl(USHORT rBeginSecCyl)
  919. {
  920.     return (   (rBeginSecCyl & 0x00C0) << 2)
  921.              + ((rBeginSecCyl & 0xFF00) >> 8);
  922. }
  923.  
  924. /*
  925.  * GetSec:
  926.  *      get sector number.
  927.  *
  928.  *      Originally contributed by Dmitry A. Steklenev.
  929.  *
  930.  *@@added V0.9.0 [umoeller]
  931.  */
  932.  
  933. static USHORT GetSec(USHORT rBeginSecCyl)
  934. {
  935.     return rBeginSecCyl & 0x003F;
  936. }
  937.  
  938. /*
  939.  *@@ doshGetBootManager:
  940.  *      this goes thru the master boot records on all
  941.  *      disks to find the boot manager partition.
  942.  *
  943.  *      Returns:
  944.  *
  945.  *      -- NO_ERROR: boot manager found; in that case,
  946.  *                   information about the boot manager
  947.  *                   is written into *pusDisk, *pusPart,
  948.  *                   *BmInfo. Any of these pointers can
  949.  *                   be NULL if you're not interested.
  950.  *
  951.  *      -- ERROR_NOT_SUPPORTED (50): boot manager not installed.
  952.  *
  953.  *      Originally contributed by Dmitry A. Steklenev.
  954.  *
  955.  *@@added V0.9.0 [umoeller]
  956.  */
  957.  
  958. APIRET doshGetBootManager(USHORT   *pusDisk,    // out: if != NULL, boot manager disk (1, 2, ...)
  959.                           USHORT   *pusPart,    // out: if != NULL, index of bmgr primary partition (0-3)
  960.                           PAR_INFO *pBmInfo)    // out: if != NULL, boot manager partition info
  961. {
  962.     APIRET          arc = NO_ERROR;
  963.     USHORT          count = doshQueryDiskCount();    // Physical disk number
  964.     MBR_INFO        MBoot;      // Master Boot
  965.     USHORT          usDisk;
  966.  
  967.     if (count > 8)              // Not above 8 disks
  968.         count = 8;
  969.  
  970.     for (usDisk = 1; usDisk <= count; usDisk++)
  971.     {
  972.         USHORT usPrim = 0;
  973.  
  974.         // for each disk, read the MBR, which has the
  975.         // primary partitions
  976.         if ((arc = doshReadSector(usDisk,
  977.                                   &MBoot,
  978.                                   0,            // head
  979.                                   0,            // cylinder
  980.                                   1)))          // sector
  981.             return arc;
  982.  
  983.         // scan primary partitions for whether
  984.         // BootManager partition exists
  985.         for (usPrim = 0; usPrim < 4; usPrim++)
  986.         {
  987.             if (MBoot.sPrtnInfo[usPrim].bFileSysCode == 0x0A)
  988.             {
  989.                 // this is boot manager:
  990.                 if (pBmInfo)
  991.                     *pBmInfo = MBoot.sPrtnInfo[usPrim];
  992.                 if (pusPart)
  993.                     *pusPart = usPrim;
  994.                 if (pusDisk)
  995.                     *pusDisk = usDisk;
  996.                 // stop scanning
  997.                 return NO_ERROR;
  998.             }
  999.         }
  1000.     }
  1001.  
  1002.     return (ERROR_NOT_SUPPORTED);
  1003. }
  1004.  
  1005. /*
  1006.  * GetPrimaryPartitions:
  1007.  *      this returns the primary partitions.
  1008.  *
  1009.  *      This gets called from doshGetPartitionsList.
  1010.  *
  1011.  *      Returns:
  1012.  *
  1013.  *      -- ERROR_INVALID_PARAMETER: BMInfo is NULL.
  1014.  *
  1015.  *      Originally contributed by Dmitry A. Steklenev.
  1016.  *
  1017.  *@@added V0.9.0 [umoeller]
  1018.  */
  1019.  
  1020. static APIRET GetPrimaryPartitions(PARTITIONINFO **pppiFirst,
  1021.                                    PARTITIONINFO **pppiThis,
  1022.                                    PUSHORT posCount,       // in/out: partition count
  1023.                                    PCHAR pcLetter,         // in/out: drive letter counter
  1024.                                    UINT BmDisk,            // in: physical disk (1, 2, 3, ...) of boot manager or null
  1025.                                    PAR_INFO* pBmInfo,      // in: info returned by doshGetBootManager or NULL
  1026.                                    UINT iDisk)             // in: system's physical disk count
  1027. {
  1028.     APIRET  arc = NO_ERROR;
  1029.  
  1030.     if (!pBmInfo)
  1031.         arc = ERROR_INVALID_PARAMETER;
  1032.     else
  1033.     {
  1034.         SYS_INFO        MName[32];  // Name Space from Boot Manager
  1035.         memset(&MName, 0, sizeof(MName));
  1036.  
  1037.         // read boot manager name table;
  1038.         // this is in the boot manager primary partition
  1039.         // at sector offset 3 (?!?)
  1040.         if (!(arc = doshReadSector(BmDisk,
  1041.                                    &MName,
  1042.                                    // head, cylinder, sector of bmgr primary partition:
  1043.                                    pBmInfo->bBeginHead,
  1044.                                    GetCyl(pBmInfo->rBeginSecCyl),
  1045.                                    GetSec(pBmInfo->rBeginSecCyl) + 3)))
  1046.         {
  1047.             // got bmgr name table:
  1048.             MBR_INFO        MBoot;      // Master Boot
  1049.             USHORT          i;
  1050.  
  1051.             // read master boot record of this disk
  1052.             if (!(arc = doshReadSector(iDisk,
  1053.                                        &MBoot,
  1054.                                        0,           // head
  1055.                                        0,           // cylinder
  1056.                                        1)))         // sector
  1057.             {
  1058.                 for (i = 0;
  1059.                      i < 4;     // there can be only four primary partitions
  1060.                      i++)
  1061.                 {
  1062.                     // skip unused partition, BootManager or Extended partition
  1063.                     if (    (MBoot.sPrtnInfo[i].bFileSysCode)  // skip unused
  1064.                         &&  (MBoot.sPrtnInfo[i].bFileSysCode != 0x0A) // skip boot manager
  1065.                         &&  (MBoot.sPrtnInfo[i].bFileSysCode != 0x05) // skip extended partition
  1066.                        )
  1067.                     {
  1068.                         BOOL fBootable = (    (pBmInfo)
  1069.                                            && (MName[(iDisk-1) * 4 + i].bootable & 0x01)
  1070.                                          );
  1071.                         // store this partition
  1072.                         if ((arc = AppendPartition(pppiFirst,
  1073.                                                    pppiThis,
  1074.                                                    posCount,
  1075.                                                    iDisk,
  1076.                                                    (fBootable)
  1077.                                                      ? (char*)&MName[(iDisk - 1) * 4 + i].name
  1078.                                                      : "",
  1079.                                                    *pcLetter,
  1080.                                                    MBoot.sPrtnInfo[i].bFileSysCode,
  1081.                                                    TRUE,        // primary
  1082.                                                    fBootable,
  1083.                                                    MBoot.sPrtnInfo[i].lTotalSects)))
  1084.                             return arc;
  1085.                     }
  1086.                 }
  1087.             }
  1088.         }
  1089.     }
  1090.  
  1091.     return arc;
  1092. }
  1093.  
  1094. /*
  1095.  * GetLogicalDrives:
  1096.  *      this returns info for the logical drives
  1097.  *      in the extended partition. This gets called
  1098.  *      from GetExtendedPartition.
  1099.  *
  1100.  *      This gets called from GetExtendedPartition.
  1101.  *
  1102.  *      Originally contributed by Dmitry A. Steklenev.
  1103.  *
  1104.  *@@added V0.9.0 [umoeller]
  1105.  */
  1106.  
  1107. static APIRET GetLogicalDrives(PARTITIONINFO **pppiFirst,
  1108.                                PARTITIONINFO **pppiThis,
  1109.                                PUSHORT posCount,
  1110.                                PCHAR pcLetter,
  1111.                                PAR_INFO* PrInfo,                    // in: MBR entry of extended partition
  1112.                                UINT PrDisk,
  1113.                                PAR_INFO* BmInfo)
  1114. {
  1115.     APIRET          arc = NO_ERROR;
  1116.     EXT_INFO        MBoot;      // Master Boot
  1117.     USHORT          i;
  1118.  
  1119.     if ((arc = doshReadSector(PrDisk,
  1120.                               &MBoot,
  1121.                               PrInfo->bBeginHead,
  1122.                               GetCyl(PrInfo->rBeginSecCyl),
  1123.                               GetSec(PrInfo->rBeginSecCyl))))
  1124.         return arc;
  1125.  
  1126.     for (i = 0; i < 4; i++)
  1127.     {
  1128.         // skip unused partition or BootManager partition
  1129.         if (    (MBoot.sPrtnInfo[i].bFileSysCode)
  1130.              && (MBoot.sPrtnInfo[i].bFileSysCode != 0x0A)
  1131.            )
  1132.         {
  1133.             BOOL    fBootable = FALSE;
  1134.             BOOL    fAssignLetter = FALSE;
  1135.  
  1136.             // special work around extended partition
  1137.             if (MBoot.sPrtnInfo[i].bFileSysCode == 0x05)
  1138.             {
  1139.                 if ((arc = GetLogicalDrives(pppiFirst,
  1140.                                             pppiThis,
  1141.                                             posCount,
  1142.                                             pcLetter,
  1143.                                             &MBoot.sPrtnInfo[i],
  1144.                                             PrDisk,
  1145.                                             BmInfo)))
  1146.                     return arc;
  1147.  
  1148.                 continue;
  1149.             }
  1150.  
  1151.             // raise driver letter if OS/2 would recognize this drive
  1152.             if (    (MBoot.sPrtnInfo[i].bFileSysCode < 0x75)
  1153.                )
  1154.                 fAssignLetter = TRUE;
  1155.  
  1156.             if (fAssignLetter)
  1157.                 (*pcLetter)++;
  1158.  
  1159.             fBootable = (   (BmInfo)
  1160.                          && ((MBoot.sBmNames[i].bootable & 0x01) != 0)
  1161.                         );
  1162.  
  1163.             if ((arc = AppendPartition(pppiFirst,
  1164.                                        pppiThis,
  1165.                                        posCount,
  1166.                                        PrDisk,
  1167.                                        (fBootable)
  1168.                                          ? (char*)&MBoot.sBmNames[i].name
  1169.                                          : "",
  1170.                                        (fAssignLetter)
  1171.                                          ? *pcLetter
  1172.                                          : ' ',
  1173.                                        MBoot.sPrtnInfo[i].bFileSysCode,
  1174.                                        FALSE,        // primary
  1175.                                        fBootable,    // bootable
  1176.                                        MBoot.sPrtnInfo[i].lTotalSects)))
  1177.                 return arc;
  1178.         }
  1179.     }
  1180.  
  1181.     return NO_ERROR;
  1182. }
  1183.  
  1184. /*
  1185.  * GetExtendedPartition:
  1186.  *      this finds the extended partition on the given
  1187.  *      drive and calls GetLogicalDrives in turn.
  1188.  *
  1189.  *      This gets called from doshGetPartitionsList.
  1190.  *
  1191.  *      Originally contributed by Dmitry A. Steklenev.
  1192.  *
  1193.  *@@added V0.9.0 [umoeller]
  1194.  */
  1195.  
  1196. static APIRET GetExtendedPartition(PARTITIONINFO **pppiFirst,
  1197.                                    PARTITIONINFO **pppiThis,
  1198.                                    PUSHORT posCount,
  1199.                                    PCHAR pcLetter,
  1200.                                    PAR_INFO* BmInfo,
  1201.                                    UINT iDisk)                // in: disk to query
  1202. {
  1203.     APIRET          arc = NO_ERROR;
  1204.     MBR_INFO        MBoot;      // Master Boot
  1205.     USHORT          i;
  1206.  
  1207.     if ((arc = doshReadSector(iDisk, &MBoot, 0, 0, 1)))
  1208.         return arc;
  1209.  
  1210.     // go thru MBR entries to find extended partition
  1211.     for (i = 0;
  1212.          i < 4;
  1213.          i++)
  1214.     {
  1215.         if (MBoot.sPrtnInfo[i].bFileSysCode == 0x05)
  1216.         {
  1217.             if ((arc = GetLogicalDrives(pppiFirst,
  1218.                                         pppiThis,
  1219.                                         posCount,
  1220.                                         pcLetter,
  1221.                                         &MBoot.sPrtnInfo[i],
  1222.                                         iDisk,
  1223.                                         BmInfo)))
  1224.                 return arc;
  1225.         }
  1226.     }
  1227.  
  1228.     return NO_ERROR;
  1229. }
  1230.  
  1231. /*
  1232.  *@@ ReadFDiskPartitions:
  1233.  *      helper for doshGetPartitionsList for non-LVM
  1234.  *      systems.
  1235.  *
  1236.  *      Originally contributed by Dmitry A. Steklenev.
  1237.  *
  1238.  *@@added V0.9.16 (2001-10-08) [umoeller]
  1239.  */
  1240.  
  1241. static APIRET ReadFDiskPartitions(PARTITIONINFO **ppPartitionInfos,
  1242.                                   USHORT *pcPartitions,
  1243.                                   PUSHORT pusContext)              // out: error context
  1244. {
  1245.     APIRET          arc = NO_ERROR;
  1246.  
  1247.     PAR_INFO        BmInfo;     // BootManager partition
  1248.     USHORT          usBmDisk;     // BootManager disk
  1249.     USHORT          cDisks = doshQueryDiskCount();    // physical disks count
  1250.     USHORT          i;
  1251.  
  1252.     CHAR            cLetter = 'C';  // first drive letter
  1253.  
  1254.     PARTITIONINFO   *ppiTemp = NULL;
  1255.  
  1256.     if (cDisks > 8)              // Not above 8 disks
  1257.         cDisks = 8;
  1258.  
  1259.     // get boot manager disk and info
  1260.     if ((arc = doshGetBootManager(&usBmDisk,
  1261.                                   NULL,
  1262.                                   &BmInfo)) != NO_ERROR)
  1263.     {
  1264.         *pusContext = 1;
  1265.     }
  1266.     else
  1267.     {
  1268.         // on each disk, read primary partitions
  1269.         for (i = 1; i <= cDisks; i++)
  1270.         {
  1271.             if ((arc = GetPrimaryPartitions(ppPartitionInfos,
  1272.                                             &ppiTemp,
  1273.                                             pcPartitions,
  1274.                                             &cLetter,
  1275.                                             usBmDisk,
  1276.                                             usBmDisk ? &BmInfo : 0,
  1277.                                             i)))
  1278.             {
  1279.                 *pusContext = 2;
  1280.             }
  1281.         }
  1282.  
  1283.         if (!arc && usBmDisk)
  1284.         {
  1285.             // boot manager found:
  1286.             // on each disk, read extended partition
  1287.             // with logical drives
  1288.             for (i = 1; i <= cDisks; i++)
  1289.             {
  1290.                 if ((arc = GetExtendedPartition(ppPartitionInfos,
  1291.                                                 &ppiTemp,
  1292.                                                 pcPartitions,
  1293.                                                 &cLetter,
  1294.                                                 &BmInfo,
  1295.                                                 i)))
  1296.                 {
  1297.                     *pusContext = 3;
  1298.                 }
  1299.             }
  1300.         }
  1301.     } // end else if ((arc = doshGetBootManager(&usBmDisk,
  1302.  
  1303.     return arc;
  1304. }
  1305.  
  1306. #endif
  1307.  
  1308. /*
  1309.  *@@ CleanPartitionInfos:
  1310.  *
  1311.  *@@added V0.9.9 (2001-04-07) [umoeller]
  1312.  */
  1313.  
  1314. static VOID CleanPartitionInfos(PPARTITIONINFO ppiThis)
  1315. {
  1316.     while (ppiThis)
  1317.     {
  1318.         PPARTITIONINFO ppiNext = ppiThis->pNext;
  1319.         free(ppiThis);
  1320.         ppiThis = ppiNext;
  1321.     }
  1322. }
  1323.  
  1324. /*
  1325.  *@@ doshGetPartitionsList:
  1326.  *      this returns lots of information about the
  1327.  *      partitions on all physical disks, which is
  1328.  *      read directly from the MBRs and partition
  1329.  *      tables.
  1330.  *
  1331.  *      If NO_ERROR is returned by this function,
  1332.  *      *ppPartitionInfo points to a linked list of
  1333.  *      PARTITIONINFO structures, which has
  1334.  *      *pusPartitionCount items.
  1335.  *
  1336.  *      In that case, use doshFreePartitionsList to
  1337.  *      free the resources allocated by this function.
  1338.  *
  1339.  *      What this function returns depends on whether
  1340.  *      LVM is installed.
  1341.  *
  1342.  *      --  If LVM.DLL is found on the LIBPATH, this opens
  1343.  *          the LVM engine and returns the info from the
  1344.  *          LVM engine in the PARTITIONINFO structures.
  1345.  *          The partitions are then sorted by disk in
  1346.  *          ascending order.
  1347.  *
  1348.  *      --  Otherwise, we parse the partition tables
  1349.  *          manually. The linked list then starts out with
  1350.  *          all the primary partitions, followed by the
  1351.  *          logical drives in the extended partitions.
  1352.  *          This function attempts to guess the correct drive
  1353.  *          letters and stores these with the PARTITIONINFO
  1354.  *          items, but there's no guarantee that this is
  1355.  *          correct. We correctly ignore Linux partitions here
  1356.  *          and give all primary partitions the C: letter, but
  1357.  *          I have no idea what happens with NTFS partitions,
  1358.  *          since I have none.
  1359.  *
  1360.  *      If an error != NO_ERROR is returned, *pusContext
  1361.  *      will be set to one of the following:
  1362.  *
  1363.  *      --  1: boot manager not found
  1364.  *
  1365.  *      --  2: primary partitions error
  1366.  *
  1367.  *      --  3: secondary partitions error
  1368.  *
  1369.  *      --  0: something else.
  1370.  *
  1371.  *      Originally contributed by Dmitry A. Steklenev.
  1372.  *
  1373.  *@@added V0.9.0 [umoeller]
  1374.  *@@changed V0.9.9 (2001-04-07) [umoeller]: added transparent LVM support; changed prototype
  1375.  *@@changed V0.9.9 (2001-04-07) [umoeller]: fixed memory leaks on errors
  1376.  */
  1377.  
  1378. APIRET doshGetPartitionsList(PPARTITIONSLIST *ppList,
  1379.                              PUSHORT pusContext)                // out: error context
  1380. {
  1381.     APIRET          arc = NO_ERROR;
  1382.  
  1383.     PLVMINFO        pLVMInfo = NULL;
  1384.  
  1385.     PARTITIONINFO   *pPartitionInfos = NULL; // linked list of all partitions
  1386.     USHORT          cPartitions = 0;        // bootable partition count
  1387.  
  1388.     if (!ppList)
  1389.         return (ERROR_INVALID_PARAMETER);
  1390.  
  1391.     if (!(arc = doshQueryLVMInfo(&pLVMInfo)))
  1392.     {
  1393.         // LVM installed:
  1394.         arc = doshReadLVMPartitions(pLVMInfo,         // in: LVM info
  1395.                                     &pPartitionInfos, // out: partitions array
  1396.                                     &cPartitions);      // out: partitions count
  1397.         // copied to output below
  1398.  
  1399.         if (arc)
  1400.         {
  1401.             // error: start over
  1402.             doshFreeLVMInfo(pLVMInfo);
  1403.             CleanPartitionInfos(pPartitionInfos);
  1404.             pPartitionInfos = NULL;
  1405.             cPartitions = 0;
  1406.         }
  1407.     }
  1408.  
  1409. #ifndef __XWPLITE__
  1410.     if (arc)
  1411.         // LVM not installed, or failed:
  1412.         // parse partitions manually
  1413.         arc = ReadFDiskPartitions(&pPartitionInfos,
  1414.                                   &cPartitions,
  1415.                                   pusContext);
  1416. #endif
  1417.  
  1418.     if (!arc)
  1419.     {
  1420.         // no error so far:
  1421.         *pusContext = 0;
  1422.  
  1423.         *ppList = NEW(PARTITIONSLIST);
  1424.         if (!(*ppList))
  1425.             arc = ERROR_NOT_ENOUGH_MEMORY;
  1426.         else
  1427.         {
  1428.             ZERO(*ppList);
  1429.  
  1430.             (*ppList)->pPartitionInfo = pPartitionInfos;
  1431.             (*ppList)->cPartitions = cPartitions;
  1432.  
  1433.             _Pmpf((__FUNCTION__ ": returning %d partitions", cPartitions));
  1434.         }
  1435.     }
  1436.  
  1437.     if (arc)
  1438.         CleanPartitionInfos(pPartitionInfos);
  1439.  
  1440.     _Pmpf((__FUNCTION__ ": exiting, arc = %d", arc));
  1441.  
  1442.     return arc;
  1443. }
  1444.  
  1445. /*
  1446.  *@@ doshFreePartitionsList:
  1447.  *      this frees the resources allocated by
  1448.  *      doshGetPartitionsList.
  1449.  *
  1450.  *@@added V0.9.0 [umoeller]
  1451.  */
  1452.  
  1453. APIRET doshFreePartitionsList(PPARTITIONSLIST ppList)
  1454. {
  1455.     if (!ppList)
  1456.         return (ERROR_INVALID_PARAMETER);
  1457.     else
  1458.     {
  1459.         CleanPartitionInfos(ppList->pPartitionInfo);
  1460.         doshFreeLVMInfo(ppList->pLVMInfo);
  1461.         free(ppList);
  1462.     }
  1463.  
  1464.     return NO_ERROR;
  1465. }
  1466.  
  1467. /********************************************************************
  1468.  *
  1469.  *   LVM declarations
  1470.  *
  1471.  ********************************************************************/
  1472.  
  1473. /*
  1474.  *@@category: Helpers\Control program helpers\Partitions info\Quick LVM Interface
  1475.  *      functions for transparently interfacing LVM.DLL.
  1476.  */
  1477.  
  1478. typedef unsigned char       BOOLEAN;
  1479. typedef unsigned short int  CARDINAL16;
  1480. typedef unsigned long       CARDINAL32;
  1481. typedef unsigned int        CARDINAL;
  1482. typedef unsigned long       DoubleWord;
  1483.  
  1484. #ifdef ADDRESS
  1485. #undef ADDRESS
  1486. #endif
  1487.  
  1488. typedef void* ADDRESS;
  1489.  
  1490. #pragma pack(1)
  1491.  
  1492. #define DISK_NAME_SIZE          20
  1493. #define FILESYSTEM_NAME_SIZE    20
  1494. #define PARTITION_NAME_SIZE     20
  1495. #define VOLUME_NAME_SIZE        20
  1496.  
  1497. /*
  1498.  *@@ Drive_Control_Record:
  1499.  *      invariant for a disk drive.
  1500.  *
  1501.  *@@added V0.9.9 (2001-04-07) [umoeller]
  1502.  */
  1503.  
  1504. typedef struct _Drive_Control_Record
  1505. {
  1506.     CARDINAL32   Drive_Number;                      // OS/2 Drive Number for this drive.
  1507.     CARDINAL32   Drive_Size;                        // The total number of sectors on the drive.
  1508.     DoubleWord   Drive_Serial_Number;               // The serial number assigned to this drive.  For info. purposes only.
  1509.     ADDRESS      Drive_Handle;                      // Handle used for operations on the disk that this record corresponds to.
  1510.     CARDINAL32   Cylinder_Count;                    // The number of cylinders on the drive.
  1511.     CARDINAL32   Heads_Per_Cylinder;                // The number of heads per cylinder for this drive.
  1512.     CARDINAL32   Sectors_Per_Track;                 // The number of sectors per track for this drive.
  1513.     BOOLEAN      Drive_Is_PRM;                      // Set to TRUE if this drive is a PRM.
  1514.     BYTE         Reserved[3];                       // Alignment.
  1515. } Drive_Control_Record;
  1516.  
  1517. /*
  1518.  *@@ Drive_Control_Array:
  1519.  *      returned by the Get_Drive_Control_Data function
  1520.  *
  1521.  *@@added V0.9.9 (2001-04-07) [umoeller]
  1522.  */
  1523.  
  1524. typedef struct _Drive_Control_Array
  1525. {
  1526.     Drive_Control_Record *   Drive_Control_Data;    // An array of drive control records.
  1527.     CARDINAL32               Count;                 // The number of entries in the array of drive control records.
  1528. } Drive_Control_Array;
  1529.  
  1530. /*
  1531.  *@@ Drive_Information_Record:
  1532.  *      defines the information that can be changed for a specific disk drive.
  1533.  *
  1534.  *@@added V0.9.9 (2001-04-07) [umoeller]
  1535.  */
  1536.  
  1537. typedef struct _Drive_Information_Record
  1538. {
  1539.     CARDINAL32   Total_Available_Sectors;           // The number of sectors on the disk which are not currently assigned to a partition.
  1540.     CARDINAL32   Largest_Free_Block_Of_Sectors;     // The number of sectors in the largest contiguous block of available sectors.
  1541.     BOOLEAN      Corrupt_Partition_Table;           // If TRUE, then the partitioning information found on the drive is incorrect!
  1542.     BOOLEAN      Unusable;                          // If TRUE, the drive's MBR is not accessible and the drive can not be partitioned.
  1543.     BOOLEAN      IO_Error;                          // If TRUE, then the last I/O operation on this drive failed!
  1544.     BOOLEAN      Is_Big_Floppy;                     // If TRUE, then the drive is a PRM formatted as a big floppy (i.e. the old style removable media support).
  1545.     char         Drive_Name[DISK_NAME_SIZE];        // User assigned name for this disk drive.
  1546. } Drive_Information_Record;
  1547.  
  1548. /*
  1549.  *@@ Partition_Information_Record:
  1550.  *
  1551.  *@@added V0.9.9 (2001-04-07) [umoeller]
  1552.  */
  1553.  
  1554. typedef struct _Partition_Information_Record
  1555. {
  1556.     ADDRESS      Partition_Handle;
  1557.             // The handle used to perform operations on this partition.
  1558.     ADDRESS      Volume_Handle;
  1559.             // If this partition is part of a volume, this will be the handle of
  1560.             // the volume.  If this partition is NOT part of a volume, then this
  1561.             // handle will be 0.
  1562.     ADDRESS      Drive_Handle;
  1563.             // The handle for the drive this partition resides on.
  1564.     DoubleWord   Partition_Serial_Number;
  1565.             // The serial number assigned to this partition.
  1566.     CARDINAL32   Partition_Start;
  1567.             // The LBA of the first sector of the partition.
  1568.     CARDINAL32   True_Partition_Size;
  1569.             // The total number of sectors comprising the partition.
  1570.     CARDINAL32   Usable_Partition_Size;
  1571.             // The size of the partition as reported to the IFSM.  This is the
  1572.             // size of the partition less any LVM overhead.
  1573.     CARDINAL32   Boot_Limit;
  1574.             // The maximum number of sectors from this block of free space that
  1575.             // can be used to create a bootable partition if you allocate from the
  1576.             // beginning of the block of free space.
  1577.     BOOLEAN      Spanned_Volume;
  1578.             // TRUE if this partition is part of a multi-partition volume.
  1579.     BOOLEAN      Primary_Partition;
  1580.             // True or False.  Any non-zero value here indicates that this partition
  1581.             // is a primary partition.  Zero here indicates that this partition is
  1582.             // a "logical drive" - i.e. it resides inside of an extended partition.
  1583.     BYTE         Active_Flag;
  1584.             // 80 = Partition is marked as being active.
  1585.             // 0 = Partition is not active.
  1586.     BYTE         OS_Flag;
  1587.             // This field is from the partition table.  It is known as the OS flag,
  1588.             // the Partition Type Field, Filesystem Type, and various other names.
  1589.             //      Values of interest
  1590.             //      If this field is: (values are in hex)
  1591.             //      07 = The partition is a compatibility partition formatted for use
  1592.             //      with an installable filesystem, such as HPFS or JFS.
  1593.             //      00 = Unformatted partition
  1594.             //      01 = FAT12 filesystem is in use on this partition.
  1595.             //      04 = FAT16 filesystem is in use on this partition.
  1596.             //      0A = OS/2 Boot Manager Partition
  1597.             //      35 = LVM partition
  1598.             //      84 = OS/2 FAT16 partition which has been relabeled by Boot Manager to "Hide" it.
  1599.     BYTE         Partition_Type;
  1600.             // 0 = Free Space
  1601.             // 1 = LVM Partition (Part of an LVM Volume.)
  1602.             // 2 = Compatibility Partition
  1603.             // All other values are reserved for future use.
  1604.     BYTE         Partition_Status;
  1605.             // 0 = Free Space
  1606.             // 1 = In Use - i.e. already assigned to a volume.
  1607.             // 2 = Available - i.e. not currently assigned to a volume.
  1608.     BOOLEAN      On_Boot_Manager_Menu;
  1609.             // Set to TRUE if this partition is not part of a Volume yet is on the
  1610.             // Boot Manager Menu.
  1611.     BYTE         Reserved;
  1612.             // Alignment.
  1613.     char         Volume_Drive_Letter;
  1614.             // The drive letter assigned to the volume that this partition is a part of.
  1615.     char         Drive_Name[DISK_NAME_SIZE];
  1616.             // User assigned name for this disk drive.
  1617.     char         File_System_Name[FILESYSTEM_NAME_SIZE];
  1618.             // The name of the filesystem in use on this partition, if it is known.
  1619.     char         Partition_Name[PARTITION_NAME_SIZE];
  1620.             // The user assigned name for this partition.
  1621.     char         Volume_Name[VOLUME_NAME_SIZE];
  1622.             // If this partition is part of a volume, then this will be the
  1623.             // name of the volume that this partition is a part of.  If this
  1624.             // record represents free space, then the Volume_Name will be
  1625.             // "FREE SPACE xx", where xx is a unique numeric ID generated by
  1626.             // LVM.DLL.  Otherwise it will be an empty string.
  1627. } Partition_Information_Record;
  1628.  
  1629. // The following defines are for use with the Partition_Type field in the
  1630. // Partition_Information_Record.
  1631. #define FREE_SPACE_PARTITION     0
  1632. #define LVM_PARTITION            1
  1633. #define COMPATIBILITY_PARTITION  2
  1634.  
  1635. // The following defines are for use with the Partition_Status field in the
  1636. // Partition_Information_Record.
  1637. #define PARTITION_IS_IN_USE      1
  1638. #define PARTITION_IS_AVAILABLE   2
  1639. #define PARTITION_IS_FREE_SPACE  0
  1640.  
  1641. /*
  1642.  *@@ Partition_Information_Array:
  1643.  *      returned by various functions in the LVM Engine.
  1644.  *
  1645.  *@@added V0.9.9 (2001-04-07) [umoeller]
  1646.  */
  1647.  
  1648. typedef struct _Partition_Information_Array
  1649. {
  1650.     Partition_Information_Record * Partition_Array; // An array of Partition_Information_Records.
  1651.     CARDINAL32                     Count;           // The number of entries in the Partition_Array.
  1652. } Partition_Information_Array;
  1653.  
  1654. /*
  1655.  *@@ Volume_Information_Record:
  1656.  *      variable information for a volume.
  1657.  *
  1658.  *@@added V0.9.9 (2001-04-07) [umoeller]
  1659.  */
  1660.  
  1661. typedef struct _Volume_Information_Record
  1662. {
  1663.     CARDINAL32 Volume_Size;
  1664.             // The number of sectors comprising the volume.
  1665.     CARDINAL32 Partition_Count;
  1666.             // The number of partitions which comprise this volume.
  1667.     CARDINAL32 Drive_Letter_Conflict;
  1668.             // 0   indicates that the drive letter preference for this volume is unique.
  1669.             // 1   indicates that the drive letter preference for this volume
  1670.             //       is not unique, but this volume got its preferred drive letter anyway.
  1671.             // 2   indicates that the drive letter preference for this volume
  1672.             //       is not unique, and this volume did NOT get its preferred drive letter.
  1673.             // 4   indicates that this volume is currently "hidden" - i.e. it has
  1674.             //       no drive letter preference at the current time.
  1675.     BOOLEAN    Compatibility_Volume;
  1676.             // TRUE if this is for a compatibility volume, FALSE otherwise.
  1677.     BOOLEAN    Bootable;
  1678.             // Set to TRUE if this volume appears on the Boot Manager menu, or if it is
  1679.             // a compatibility volume and its corresponding partition is the first active
  1680.             // primary partition on the first drive.
  1681.     char       Drive_Letter_Preference;
  1682.             // The drive letter that this volume desires to be.
  1683.     char       Current_Drive_Letter;
  1684.             // The drive letter currently used to access this volume.
  1685.             // May be different than Drive_Letter_Preference if there was a conflict ( i.e. Drive_Letter_Preference
  1686.             // is already in use by another volume ).
  1687.     char       Initial_Drive_Letter;
  1688.             // The drive letter assigned to this volume by the operating system
  1689.             // when LVM was started. This may be different from the
  1690.             // Drive_Letter_Preference if there were conflicts, and
  1691.             // may be different from the Current_Drive_Letter.  This
  1692.             // will be 0x0 if the Volume did not exist when the LVM Engine
  1693.             // was opened (i.e. it was created during this LVM session).
  1694.     BOOLEAN    New_Volume;
  1695.             // Set to FALSE if this volume existed before the LVM Engine was
  1696.             // opened.  Set to TRUE if this volume was created after the LVM
  1697.             // Engine was opened.
  1698.     BYTE       Status;
  1699.             // 0 = None.
  1700.             // 1 = Bootable
  1701.             // 2 = Startable
  1702.             // 3 = Installable.
  1703.     BYTE       Reserved_1;
  1704.     char       Volume_Name[VOLUME_NAME_SIZE];
  1705.             // The user assigned name for this volume.
  1706.     char       File_System_Name[FILESYSTEM_NAME_SIZE];
  1707.             // The name of the filesystem in use on this partition, if it
  1708.             // is known.
  1709. } Volume_Information_Record;
  1710.  
  1711. #pragma pack()
  1712.  
  1713. /********************************************************************
  1714.  *
  1715.  *   Quick LVM Interface API
  1716.  *
  1717.  ********************************************************************/
  1718.  
  1719. /*
  1720.  *@@ LVMINFOPRIVATE:
  1721.  *      private structure used by doshQueryLVMInfo.
  1722.  *      This is what the LVMINFO pointer really
  1723.  *      points to.
  1724.  *
  1725.  *@@added V0.9.9 (2001-04-07) [umoeller]
  1726.  */
  1727.  
  1728. typedef struct _LVMINFOPRIVATE
  1729. {
  1730.     LVMINFO             LVMInfo;            // public structure (dosh.h)
  1731.  
  1732.     // function pointers resolved from LVM.DLL
  1733.  
  1734.     void (* _System     Open_LVM_Engine)(BOOLEAN Ignore_CHS,
  1735.                                          CARDINAL32 *Error_Code);
  1736.  
  1737.     void (* _System     Free_Engine_Memory)(ADDRESS Object);
  1738.  
  1739.     void (* _System     Close_LVM_Engine)(void);
  1740.  
  1741.     Drive_Control_Array (* _System
  1742.                         Get_Drive_Control_Data)(CARDINAL32 *Error_Code);
  1743.  
  1744.     Drive_Information_Record (* _System
  1745.                         Get_Drive_Status)(ADDRESS Drive_Handle,
  1746.                                           CARDINAL32 *Error_Code);
  1747.  
  1748.     Partition_Information_Array (* _System
  1749.                         Get_Partitions)(ADDRESS Handle,
  1750.                                         CARDINAL32 *Error_Code);
  1751.  
  1752.     Volume_Information_Record (*_System
  1753.                         Get_Volume_Information)(ADDRESS Volume_Handle,
  1754.                                                 CARDINAL32 *Error_Code);
  1755.  
  1756. } LVMINFOPRIVATE, *PLVMINFOPRIVATE;
  1757.  
  1758. #define LVM_ERROR_FIRST             20000
  1759.  
  1760. /*
  1761.  *@@ doshQueryLVMInfo:
  1762.  *      creates an LVMINFO structure if LVM is installed.
  1763.  *      Returns that structure (which the caller must free
  1764.  *      using doshFreeLVMInfo) or NULL if LVM.DLL was not
  1765.  *      found along the LIBPATH.
  1766.  *
  1767.  *@@added V0.9.9 (2001-04-07) [umoeller]
  1768.  */
  1769.  
  1770. APIRET doshQueryLVMInfo(PLVMINFO *ppLVMInfo)
  1771. {
  1772.     APIRET          arc = NO_ERROR;
  1773.     CHAR            szError[100];
  1774.     PLVMINFOPRIVATE pLVMInfo = NULL;
  1775.     HMODULE         hmodLVM = NULLHANDLE;
  1776.  
  1777.     if (!(arc = DosLoadModule(szError,
  1778.                               sizeof(szError),
  1779.                               "LVM",
  1780.                               &hmodLVM)))
  1781.     {
  1782.         // got LVM.DLL:
  1783.         pLVMInfo = NEW(LVMINFOPRIVATE);
  1784.         if (!pLVMInfo)
  1785.             arc = ERROR_NOT_ENOUGH_MEMORY;
  1786.         else
  1787.         {
  1788.             // array of function pointers to be resolved from LVM.DLL
  1789.             RESOLVEFUNCTION     aFunctions[] =
  1790.                     {
  1791.                         "Open_LVM_Engine", (PFN*)&pLVMInfo->Open_LVM_Engine,
  1792.                         "Free_Engine_Memory", (PFN*)&pLVMInfo->Free_Engine_Memory,
  1793.                         "Close_LVM_Engine", (PFN*)&pLVMInfo->Close_LVM_Engine,
  1794.                         "Get_Drive_Control_Data", (PFN*)&pLVMInfo->Get_Drive_Control_Data,
  1795.                         "Get_Drive_Status", (PFN*)&pLVMInfo->Get_Drive_Status,
  1796.                         "Get_Partitions", (PFN*)&pLVMInfo->Get_Partitions,
  1797.                         "Get_Volume_Information", (PFN*)&pLVMInfo->Get_Volume_Information
  1798.                     };
  1799.             ULONG               ul;
  1800.  
  1801.             ZERO(pLVMInfo);
  1802.  
  1803.             pLVMInfo->LVMInfo.hmodLVM = hmodLVM;
  1804.  
  1805.             // now resolve function pointers
  1806.             for (ul = 0;
  1807.                  ul < ARRAYITEMCOUNT(aFunctions);
  1808.                  ul++)
  1809.             {
  1810.                 PRESOLVEFUNCTION pFuncThis = &aFunctions[ul];
  1811.                 arc = DosQueryProcAddr(hmodLVM,
  1812.                                        0,               // ordinal, ignored
  1813.                                        (PSZ)pFuncThis->pcszFunctionName,
  1814.                                        pFuncThis->ppFuncAddress);
  1815.                 if (!pFuncThis->ppFuncAddress)
  1816.                     arc = ERROR_INVALID_NAME;
  1817.  
  1818.                 if (arc)
  1819.                     break;
  1820.             }
  1821.         }
  1822.     }
  1823.  
  1824.     if (arc)
  1825.         doshFreeLVMInfo((PLVMINFO)pLVMInfo);
  1826.     else
  1827.         *ppLVMInfo = (PLVMINFO)pLVMInfo;
  1828.  
  1829.     return arc;
  1830. }
  1831.  
  1832. /*
  1833.  *@@ doshReadLVMPartitions:
  1834.  *      using the LVMINFO parameter from doshQueryLVMInfo,
  1835.  *      builds an array of PARTITIONINFO structures with
  1836.  *      the data returned from LVM.DLL.
  1837.  *
  1838.  *@@added V0.9.9 (2001-04-07) [umoeller]
  1839.  */
  1840.  
  1841. APIRET doshReadLVMPartitions(PLVMINFO pInfo,         // in: LVM info
  1842.                              PPARTITIONINFO *ppPartitionInfo, // out: partitions array
  1843.                              PUSHORT pcPartitions)      // out: partitions count
  1844. {
  1845.     APIRET          arc = NO_ERROR;
  1846.     CARDINAL32      Error = 0;
  1847.  
  1848.     PARTITIONINFO   *pPartitionInfos = NULL, // linked list of all partitions
  1849.                     *ppiTemp = NULL;
  1850.     USHORT          cPartitions = 0;        // bootable partition count
  1851.     PLVMINFOPRIVATE pLVMInfo = (PLVMINFOPRIVATE)pInfo;
  1852.  
  1853.     _Pmpf((__FUNCTION__ ": entering"));
  1854.  
  1855.     if (!pLVMInfo)
  1856.         return (ERROR_INVALID_PARAMETER);
  1857.  
  1858.     // initialize LVM engine
  1859.     pLVMInfo->Open_LVM_Engine(TRUE,
  1860.                               &Error);
  1861.  
  1862.     _Pmpf(("  Open_LVM_Engine Error: %d"));
  1863.  
  1864.     if (!Error)
  1865.     {
  1866.         Drive_Control_Array DCA = pLVMInfo->Get_Drive_Control_Data(&Error);
  1867.                         // member records to be freed
  1868.  
  1869.         _Pmpf(("  Get_Drive_Control_Data Error: %d, drive count: %d", Error, DCA.Count));
  1870.  
  1871.         if (    (!Error)
  1872.              && (DCA.Count)
  1873.            )
  1874.         {
  1875.             // DCA.Drive_Control_Data now contains drive information records;
  1876.             // this must be freed
  1877.             ULONG   ulDisk;
  1878.  
  1879.             for (ulDisk = 0;
  1880.                  ulDisk < DCA.Count;
  1881.                  ulDisk++)
  1882.             {
  1883.                 Drive_Control_Record *pDriveControlRecord
  1884.                     = &DCA.Drive_Control_Data[ulDisk];
  1885.                 ADDRESS hDrive = pDriveControlRecord->Drive_Handle;
  1886.  
  1887.                 /* Drive_Information_Record pDriveInfoRecord
  1888.                     = pLVMInfo->Get_Drive_Status(hDrive,
  1889.                                                  &Error);
  1890.  
  1891.                 _Pmpf(("  drive %d Get_Drive_Status Error: %d", ulDisk, Error));
  1892.  
  1893.                 if (!Error) */
  1894.                 {
  1895.                     Partition_Information_Array PIA
  1896.                         = pLVMInfo->Get_Partitions(hDrive,
  1897.                                                    &Error);
  1898.  
  1899.                     _Pmpf(("    Get_Partitions Error: %d", Error));
  1900.  
  1901.                     if (!Error)
  1902.                     {
  1903.                         // PIA.Partition_Array now contains
  1904.                         // Partition_Information_Record; must be freed
  1905.  
  1906.                         // now go thru partitions of this drive
  1907.                         ULONG ulPart;
  1908.                         for (ulPart = 0;
  1909.                              ulPart < PIA.Count;
  1910.                              ulPart++)
  1911.                         {
  1912.                             Partition_Information_Record *pPartition
  1913.                                 = &PIA.Partition_Array[ulPart];
  1914.                             Volume_Information_Record VolumeInfo;
  1915.  
  1916.                             const char  *pcszBootName = NULL; // for now
  1917.                             BOOL        fBootable = FALSE;
  1918.  
  1919.                             if (pPartition->Volume_Handle)
  1920.                             {
  1921.                                 // this partition is part of a volume:
  1922.                                 // only then can it be bootable...
  1923.                                 // get the volume info
  1924.                                 VolumeInfo
  1925.                                     = pLVMInfo->Get_Volume_Information(pPartition->Volume_Handle,
  1926.                                                                        &Error);
  1927.                                 pcszBootName = VolumeInfo.Volume_Name;
  1928.  
  1929.                                 fBootable = (VolumeInfo.Status == 1);
  1930.                             }
  1931.  
  1932.  
  1933.                             if (arc = AppendPartition(&pPartitionInfos,
  1934.                                                       &ppiTemp,
  1935.                                                       &cPartitions,
  1936.                                                       ulDisk + 1,
  1937.                                                       pcszBootName,
  1938.                                                       pPartition->Volume_Drive_Letter,
  1939.                                                       pPartition->OS_Flag,  // FS type
  1940.                                                       pPartition->Primary_Partition,
  1941.                                                       fBootable,
  1942.                                                       pPartition->True_Partition_Size))
  1943.                                 break;
  1944.                         }
  1945.  
  1946.                         // clean up partitions
  1947.                         pLVMInfo->Free_Engine_Memory(PIA.Partition_Array);
  1948.                     }
  1949.                 }
  1950.                 /* else
  1951.                     // error:
  1952.                     break; */
  1953.             }
  1954.  
  1955.             // clean up drive data
  1956.             pLVMInfo->Free_Engine_Memory(DCA.Drive_Control_Data);
  1957.         }
  1958.     }
  1959.  
  1960.     // close LVM
  1961.     pLVMInfo->Close_LVM_Engine();
  1962.  
  1963.     if (Error)
  1964.     {
  1965.         // if we got an error, return it with the
  1966.         // LVM error offset
  1967.         arc = LVM_ERROR_FIRST + Error;
  1968.  
  1969.         CleanPartitionInfos(pPartitionInfos);
  1970.     }
  1971.  
  1972.     if (!arc)
  1973.     {
  1974.         *ppPartitionInfo = pPartitionInfos;
  1975.         *pcPartitions = cPartitions;
  1976.     }
  1977.  
  1978.     _Pmpf((__FUNCTION__ ": exiting, arg = %d", arc));
  1979.  
  1980.     return arc;
  1981. }
  1982.  
  1983. /*
  1984.  *@@ doshFreeLVMInfo:
  1985.  *
  1986.  *@@added V0.9.9 (2001-04-07) [umoeller]
  1987.  */
  1988.  
  1989. VOID doshFreeLVMInfo(PLVMINFO pInfo)
  1990. {
  1991.     if (pInfo)
  1992.     {
  1993.         if (pInfo->hmodLVM)
  1994.             DosFreeModule(pInfo->hmodLVM);
  1995.  
  1996.         free(pInfo);
  1997.     }
  1998. }
  1999.  
  2000. /*
  2001.  *@@category: Helpers\Control program helpers\Wildcard matching
  2002.  *      See doshMatch.
  2003.  */
  2004.  
  2005. /* ******************************************************************
  2006.  *
  2007.  *   Wildcard matching
  2008.  *
  2009.  ********************************************************************/
  2010.  
  2011. /*
  2012.  * PerformMatch:
  2013.  *      compares a single path component. The input strings must
  2014.  *      not have slashes or backslashes in them.
  2015.  *
  2016.  *      fHasDot must be true if pName contains at least one dot.
  2017.  *
  2018.  *      Note that this function is recursive.
  2019.  */
  2020.  
  2021. static BOOL PerformMatch(PCSZ pMask,
  2022.                          PCSZ pName,
  2023.                          int fHasDot)
  2024. {
  2025.     while (TRUE)
  2026.     {
  2027.         // go thru the pMask char by char
  2028.         switch (*pMask)
  2029.         {
  2030.             case 0:
  2031.                 // if we've reached the end of the mask,
  2032.                 // we better have the end of the name too
  2033.                 if (*pName == 0)
  2034.                     return TRUE;
  2035.                 return FALSE;
  2036.  
  2037.             case '?':
  2038.                 // a question mark matches one single character;
  2039.                 // it does _not_ match a dot;
  2040.                 // at the end of the component, it also matches
  2041.                 // no characters
  2042.                 if (    (*pName != '.')
  2043.                      && (*pName != 0)
  2044.                    )
  2045.                     ++pName;
  2046.                 ++pMask;
  2047.             break;
  2048.  
  2049.             case '*':
  2050.                 // asterisk matches zero or more characters
  2051.  
  2052.                 // skip extra asterisks
  2053.                 /*
  2054.                 do
  2055.                 {
  2056.                     ++pMask;
  2057.                 } while (*pMask == '*');
  2058.                 */
  2059.  
  2060.                 while (*(++pMask) == '*')       // V0.9.20 (2002-07-25) [umoeller]
  2061.                     ;
  2062.  
  2063.                 // pMask points to after '*';
  2064.                 // pName is unchanged... so for each pName
  2065.                 // that follows, check if it matches
  2066.                 while (TRUE)
  2067.                 {
  2068.                     if (PerformMatch(pMask, pName, fHasDot))
  2069.                         // the remainder matched:
  2070.                         // then everything matches
  2071.                         return TRUE;
  2072.  
  2073.                     if (*pName == 0)
  2074.                         return FALSE;
  2075.  
  2076.                     // didn't match: try next pName
  2077.                     ++pName;
  2078.                 }
  2079.  
  2080.             case '.':
  2081.                 // a dot matches a dot only, even if the name doesn't
  2082.                 // have one at the end
  2083.                 ++pMask;
  2084.                 if (*pName == '.')
  2085.                     ++pName;
  2086.                 else if (    (fHasDot)
  2087.                           || (*pName != 0)
  2088.                         )
  2089.                     return FALSE;
  2090.             break;
  2091.  
  2092.             default:
  2093.                 if (*pMask++ != *pName++)
  2094.                     return FALSE;
  2095.             break;
  2096.         }
  2097.     }
  2098. }
  2099.  
  2100. /*
  2101.  *@@ doshMatchCase:
  2102.  *      this matches '*' and '?' wildcards, similar to what
  2103.  *      DosEditName does. However, this does not require a
  2104.  *      file to be present, but works on strings only.
  2105.  *
  2106.  *      Returns TRUE if the given name matches the given mask.
  2107.  *
  2108.  *      This accepts both short and fully qualified masks and
  2109.  *      names, but the following rules apply:
  2110.  *
  2111.  *      --  Either both the mask and the name must be fully
  2112.  *          qualified, or both must not. Otherwise the match fails.
  2113.  *
  2114.  *      --  If fully qualified, only the last component may contain
  2115.  *          wildcards.
  2116.  *
  2117.  *      --  This compares WITH respect to case always. Upper-case
  2118.  *          both the mask and the name before calling this, or
  2119.  *          use doshMatch instead.
  2120.  *
  2121.  *      --  As opposed to the WPS, this handles multiple dots in
  2122.  *          filenames correctly. For example, the WPS will not
  2123.  *          match "*.ZIP" against "whatever-0.9.3.zip", but this
  2124.  *          one will.
  2125.  *
  2126.  *      This replaces strhMatchOS2 which has been removed with
  2127.  *      V0.9.16 and is a lot faster than the old code, which has
  2128.  *      been completely rewritten.
  2129.  *
  2130.  *@@added V0.9.16 (2002-01-01) [umoeller]
  2131.  */
  2132.  
  2133. BOOL doshMatchCase(const char *pcszMask,     // in: mask (e.g. "*.TXT")
  2134.                    const char *pcszName)     // in: string to check (e.g. "TEST.TXT")
  2135. {
  2136.     BOOL    brc = FALSE;
  2137.  
  2138.     PCSZ    pLastMaskComponent,
  2139.             pLastNameComponent;
  2140.  
  2141.     ULONG   cbMaskPath = 0,
  2142.             cbNamePath = 0;
  2143.  
  2144.     if (pLastMaskComponent = strrchr(pcszMask, '\\'))
  2145.     {
  2146.         // length of path component
  2147.         cbMaskPath = pLastMaskComponent - pcszMask;
  2148.         pLastMaskComponent++;
  2149.     }
  2150.     else
  2151.         pLastMaskComponent = pcszMask;
  2152.  
  2153.     if (pLastNameComponent = strrchr(pcszName, '\\'))
  2154.     {
  2155.         // length of path component
  2156.         cbNamePath = pLastNameComponent - pcszName;
  2157.         pLastNameComponent++;
  2158.     }
  2159.     else
  2160.         pLastNameComponent = pcszName;
  2161.  
  2162.     // compare paths; if the lengths are different
  2163.     // or memcmp fails, we can't match
  2164.     if (    (cbMaskPath == cbNamePath)      // can both be null
  2165.          && (    (cbMaskPath == 0)
  2166.               || (!memcmp(pcszMask, pcszName, cbMaskPath))
  2167.             )
  2168.        )
  2169.     {
  2170.         // alright, paths match:
  2171.         brc = PerformMatch(pLastMaskComponent,
  2172.                            pLastNameComponent,
  2173.                            // has dot?
  2174.                            (strchr(pLastNameComponent, '.') != NULL));
  2175.  
  2176.     }
  2177.  
  2178.     return brc;
  2179. }
  2180.  
  2181. /*
  2182.  *@@ doshMatchCaseNoPath:
  2183.  *      like doshMatchCase, but is faster if you are sure that
  2184.  *      neither pcszMask nor pcszName contain path separators
  2185.  *      ("\" characters). In other words, this is for short
  2186.  *      filenames.
  2187.  *
  2188.  *@@added V0.9.20 (2002-07-25) [umoeller]
  2189.  */
  2190.  
  2191. BOOL doshMatchCaseNoPath(const char *pcszMask,     // in: mask (e.g. "*.TXT")
  2192.                          const char *pcszName)     // in: string to check (e.g. "TEST.TXT")
  2193. {
  2194.     return PerformMatch(pcszMask,
  2195.                         pcszName,
  2196.                         // has dot?
  2197.                         (strchr(pcszName, '.') != NULL));
  2198. }
  2199.  
  2200. /*
  2201.  *@@ doshMatch:
  2202.  *      like doshMatchCase, but compares without respect
  2203.  *      to case.
  2204.  *
  2205.  *@@added V0.9.16 (2002-01-26) [umoeller]
  2206.  */
  2207.  
  2208. BOOL doshMatch(const char *pcszMask,     // in: mask (e.g. "*.TXT")
  2209.                const char *pcszName)     // in: string to check (e.g. "TEST.TXT")
  2210. {
  2211.     ULONG   cbMask = strlen(pcszMask),
  2212.             cbName = strlen(pcszName);
  2213.     PSZ     pszMask = (PSZ)_alloca(cbMask + 1),
  2214.             pszName = (PSZ)_alloca(cbName + 1);
  2215.  
  2216.     memcpy(pszMask, pcszMask, cbMask + 1);
  2217.     nlsUpper(pszMask);
  2218.     memcpy(pszName, pcszName, cbName + 1);
  2219.     nlsUpper(pszName);
  2220.  
  2221.     return doshMatchCase(pszMask,
  2222.                          pszName);
  2223. }
  2224.