home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: WPS_PM / WPS_PM.zip / xfld085s.zip / helpers / dosh.c < prev    next >
C/C++ Source or Header  |  1999-02-23  |  35KB  |  1,102 lines

  1.  
  2. /*
  3.  *@@sourcefile dosh.c:
  4.  *      dosh.c contains helper functions that are independent
  5.  *      of a single application, i.e. you can use them in any
  6.  *      program. All of the following work with OS/2 only.
  7.  *
  8.  *      Function prefixes (new with V0.81):
  9.  *      --  dosh*   Dos (Control Program) helper functions
  10.  *
  11.  *      These funcs are forward-declared in dosh.h, which
  12.  *      must be #include'd first.
  13.  *
  14.  *      Save dosh.h to enforce a complete recompile of XFolder.
  15.  *      That header is checked for in the main makefile.
  16.  *
  17.  *@@include #define INCL_DOSPROCESS
  18.  *@@include #include <os2.h>
  19.  *@@include #include "dosh.h"
  20.  */
  21.  
  22. /*
  23.  *      Copyright (C) 1997-99 Ulrich Möller.
  24.  *      This file is part of the XFolder source package.
  25.  *      XFolder is free software; you can redistribute it and/or modify
  26.  *      it under the terms of the GNU General Public License as published
  27.  *      by the Free Software Foundation, in version 2 as it comes in the
  28.  *      "COPYING" file of the XFolder main distribution.
  29.  *      This program is distributed in the hope that it will be useful,
  30.  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
  31.  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  32.  *      GNU General Public License for more details.
  33.  */
  34.  
  35. #define INCL_DOSDEVIOCTL
  36. #define INCL_DOS
  37. #define INCL_DOSERRORS
  38. #define INCL_WIN
  39. #define INCL_GPI
  40. #include <os2.h>
  41. #include <stdlib.h>
  42. #include <string.h>
  43. #include <stdio.h>
  44. #include <ctype.h>
  45.  
  46. #include "dosh.h"
  47.  
  48. // #define _PMPRINTF_
  49. #include "pmprintf.h"
  50.  
  51. const CHAR acDriveLetters[28] = " ABCDEFGHIJKLMNOPQRSTUVWXYZ";
  52.  
  53. /*
  54.  *@@ doshGetULongTime:
  55.  *      this returns the current time as a ULONG value (in milliseconds).
  56.  *      Useful for stopping how much time the machine has spent in
  57.  *      a certain function. To do this, simply call this function twice,
  58.  *      and subtract the two values, which will give you the execution
  59.  *      time in milliseconds.
  60.  */
  61.  
  62. ULONG doshGetULongTime(VOID) {
  63.     DATETIME    dt;
  64.     DosGetDateTime(&dt);
  65.     return (10*(dt.hundredths + 100*(dt.seconds + 60*(dt.minutes + 60*(dt.hours)))));
  66. }
  67.  
  68. /*
  69.  *@@ doshQueryShiftState:
  70.  *      returns TRUE if any of the SHIFT keys are
  71.  *      currently pressed.
  72.  */
  73.  
  74. BOOL doshQueryShiftState(VOID)
  75. {
  76.     // BYTE abBuf[512];
  77.     HFILE hfKbd;
  78.     ULONG ulAction; //, cbRead, cbWritten;
  79.     SHIFTSTATE      ShiftState;
  80.     ULONG           cbDataLen = sizeof(ShiftState);
  81.  
  82.     DosOpen("KBD$", &hfKbd, &ulAction, 0,
  83.              FILE_NORMAL, FILE_OPEN,
  84.              OPEN_ACCESS_READONLY | OPEN_SHARE_DENYWRITE,
  85.              (PEAOP2)NULL);
  86.  
  87.     DosDevIOCtl(hfKbd, IOCTL_KEYBOARD, KBD_GETSHIFTSTATE,
  88.                       NULL, 0, NULL,      // no parameters
  89.                       &ShiftState, cbDataLen, &cbDataLen);
  90.  
  91.     DosClose(hfKbd);
  92.  
  93.     return ((ShiftState.fsState & 3) != 0);
  94. }
  95.  
  96.  
  97. /*
  98.  *@@ doshIsWarp4:
  99.  *      returns TRUE only if at least OS/2 Warp 4 is running.
  100.  */
  101.  
  102. BOOL doshIsWarp4(VOID)
  103. {
  104.     ULONG       aulBuf[3];
  105.  
  106.     DosQuerySysInfo(QSV_VERSION_MAJOR, QSV_VERSION_MINOR,
  107.                     &aulBuf, sizeof(aulBuf));
  108.     return ((aulBuf[0] != 20) || (aulBuf[1] > 30));
  109. }
  110.  
  111. /*
  112.  *@@ doshEnumDrives:
  113.  *      this copies the drive letters of the drives on the
  114.  *      system to pszBuffer which match the given pszFileSystem
  115.  *      string (for example, "HPFS"). If pszFileSystem
  116.  *      is NULL, all drives are enumerated.
  117.  *      pszBuffer should be 27 characters in size to hold
  118.  *      information for all drives.
  119.  */
  120.  
  121. VOID doshEnumDrives(PSZ pszBuffer,      // out: drive letters
  122.                     PSZ pszFileSystem)  // in: FS's to match or NULL
  123. {
  124.     CHAR szName[5] = "";
  125.     ULONG ulLogicalDrive = 3, // start with drive C:
  126.           ulFound = 0;        // found drives count
  127.     APIRET arc             = NO_ERROR; // return code
  128.  
  129.     // go thru the drives, start with C: (== 3)
  130.     do {
  131.         UCHAR nonRemovable=0;
  132.         ULONG parmSize=2;
  133.         ULONG dataLen=1;
  134.  
  135.         #pragma pack(1)
  136.         struct
  137.         {
  138.             UCHAR dummy,drive;
  139.         } parms;
  140.         #pragma pack()
  141.  
  142.         parms.drive=(UCHAR)(ulLogicalDrive-1);
  143.         arc = DosDevIOCtl((HFILE)-1,
  144.                         IOCTL_DISK,
  145.                         DSK_BLOCKREMOVABLE,
  146.                         &parms,
  147.                         parmSize,
  148.                         &parmSize,
  149.                         &nonRemovable,
  150.                         1,
  151.                         &dataLen);
  152.  
  153.         if (arc == NO_ERROR)
  154.         {
  155.             if (nonRemovable) {
  156.                 ULONG  ulOrdinal       = 0;     // ordinal of entry in name list
  157.                 BYTE   fsqBuffer[sizeof(FSQBUFFER2) + (3 * CCHMAXPATH)] = {0};
  158.                 ULONG  cbBuffer   = sizeof(fsqBuffer);        // Buffer length)
  159.                 PFSQBUFFER2 pfsqBuffer = (PFSQBUFFER2)fsqBuffer;
  160.  
  161.                 szName[0] = acDriveLetters[ulLogicalDrive];
  162.                 szName[1] = ':';
  163.                 szName[2] = '\0';
  164.  
  165.                 arc = DosQueryFSAttach(
  166.                                 szName,          // logical drive of attached FS
  167.                                 ulOrdinal,       // ignored for FSAIL_QUERYNAME
  168.                                 FSAIL_QUERYNAME, // return data for a Drive or Device
  169.                                 pfsqBuffer,      // returned data
  170.                                 &cbBuffer);      // returned data length
  171.  
  172.                 if (arc == NO_ERROR)
  173.                 {
  174.                     // The data for the last three fields in the FSQBUFFER2
  175.                     // structure are stored at the offset of fsqBuffer.szName.
  176.                     // Each data field following fsqBuffer.szName begins
  177.                     // immediately after the previous item.
  178.                     CHAR* pszFSDName = (PSZ)&(pfsqBuffer->szName) + (pfsqBuffer->cbName) + 1;
  179.                     if (pszFileSystem == NULL) {
  180.                         // enum-all mode: always copy
  181.                         pszBuffer[ulFound] = szName[0]; // drive letter
  182.                         ulFound++;
  183.                     } else if (strcmp(pszFSDName, pszFileSystem) == 0) {
  184.                         pszBuffer[ulFound] = szName[0]; // drive letter
  185.                         ulFound++;
  186.                     }
  187.                 }
  188.             }
  189.             ulLogicalDrive++;
  190.         }
  191.     } while ( arc == NO_ERROR );
  192.  
  193.     pszBuffer[ulFound] = '\0';
  194. }
  195.  
  196. /*
  197.  *@@ doshQueryBootDrive:
  198.  *      returns the letter of the boot drive
  199.  */
  200.  
  201. CHAR doshQueryBootDrive(VOID)
  202. {
  203.     ULONG ulBootDrive;
  204.     DosQuerySysInfo(5, 5, &ulBootDrive, sizeof(ulBootDrive));
  205.     return (acDriveLetters[ulBootDrive]);
  206. }
  207.  
  208. /*
  209.  *@@ doshIsFixedDisk:
  210.  *      checks whether a disk is fixed or removeable.
  211.  *      ulLogicalDrive must be 1 for drive A:, 2 for B:, ...
  212.  *      The result is stored in *pfFixed.
  213.  *      Returns DOS error code.
  214.  *      Warning: This uses DosDevIOCtl, which has proved
  215.  *      to cause problems with some device drivers for
  216.  *      removeable disks.
  217.  */
  218.  
  219. APIRET doshIsFixedDisk(ULONG  ulLogicalDrive,        // in: 1 for A:, 2 for B:, 3 for C:, ...
  220.                         PBOOL  pfFixed)             // out: TRUE for fixed disks
  221. {
  222.     APIRET arc = ERROR_INVALID_DRIVE;
  223.  
  224.     if (ulLogicalDrive) {
  225.         // parameter packet
  226.         struct {
  227.             UCHAR command, drive;
  228.         } parms;
  229.  
  230.         // data packet
  231.         UCHAR ucNonRemoveable;
  232.  
  233.         ULONG ulParmSize = sizeof(parms);
  234.         ULONG ulDataSize = sizeof(ucNonRemoveable);
  235.  
  236.         parms.drive = (UCHAR)(ulLogicalDrive-1);
  237.         arc = DosDevIOCtl((HFILE)-1,
  238.                         IOCTL_DISK,
  239.                         DSK_BLOCKREMOVABLE,
  240.                         &parms,
  241.                         ulParmSize,
  242.                         &ulParmSize,
  243.                         &ucNonRemoveable,
  244.                         ulDataSize,
  245.                         &ulDataSize);
  246.  
  247.         if (arc == NO_ERROR)
  248.             *pfFixed = (BOOL)ucNonRemoveable;
  249.     }
  250.  
  251.     return (arc);
  252. }
  253.  
  254. /*
  255.  *@@ doshAssertDrive:
  256.  *      this checks for whether the given drive
  257.  *      is currently available. If fSuppress == TRUE,
  258.  *      DosError is used in a critical section to
  259.  *      avoid "Drive not ready" popups.
  260.  */
  261.  
  262. APIRET doshAssertDrive(ULONG ulLogicalDrive, // in: 1 for A:, 2 for B:, 3 for C:, ...
  263.                        BOOL fSuppress)       // in: suppress-popups flag
  264. {
  265.     FSALLOCATE  fsa;
  266.     APIRET arc = NO_ERROR;
  267.  
  268.     if (fSuppress) {
  269.         DosError(FERR_DISABLEHARDERR | FERR_DISABLEEXCEPTION);
  270.         DosEnterCritSec();
  271.     }
  272.  
  273.     arc = DosQueryFSInfo(ulLogicalDrive, FSIL_ALLOC, &fsa, sizeof(fsa));
  274.  
  275.     if (fSuppress) {
  276.         DosError(FERR_ENABLEHARDERR | FERR_ENABLEEXCEPTION);
  277.         DosExitCritSec();
  278.     }
  279.  
  280.     return (arc);
  281. }
  282.  
  283. /*
  284.  *@@ doshQueryDiskFree:
  285.  *       returns the number of bytes remaining on the disk
  286.  *       specified by the given logical drive, or -1 upon errors.
  287.  *       Note: This returns a "double" value, because a ULONG
  288.  *       can only hold values of some 4 billion, which would
  289.  *       lead to funny results for drives > 4 GB.
  290.  */
  291.  
  292. double doshQueryDiskFree(ULONG ulLogicalDrive) // in: 1 for A:, 2 for B:, 3 for C:, ...
  293. {
  294.     FSALLOCATE  fsa;
  295.     double      dbl = -1;
  296.  
  297.     if (ulLogicalDrive)
  298.         if (DosQueryFSInfo(ulLogicalDrive, FSIL_ALLOC, &fsa, sizeof(fsa))
  299.                 == NO_ERROR)
  300.             dbl = (fsa.cSectorUnit * fsa.cbSector * fsa.cUnitAvail);
  301.  
  302.     return (dbl);
  303. }
  304.  
  305. /*
  306.  *@@ doshQueryDiskFSType:
  307.  *       copies the file-system type of the given disk object
  308.  *       (HPFS, FAT, CDFS etc.) to pszBuf.
  309.  *       Returns the DOS error code.
  310.  */
  311.  
  312. APIRET doshQueryDiskFSType(ULONG ulLogicalDrive, // in:  1 for A:, 2 for B:, 3 for C:, ...
  313.                            PSZ   pszBuf)         // out: buffer for FS type
  314. {
  315.     APIRET arc = NO_ERROR;
  316.     CHAR szName[5];
  317.  
  318.     szName[0] = acDriveLetters[ulLogicalDrive];
  319.     szName[1] = ':';
  320.     szName[2] = '\0';
  321.  
  322.     {
  323.         ULONG  ulOrdinal       = 0;     // ordinal of entry in name list
  324.         // PBYTE  pszFSDName      = NULL;  // pointer to FS name
  325.         // PBYTE  prgFSAData      = NULL;  // pointer to FS data
  326.         BYTE   fsqBuffer[sizeof(FSQBUFFER2) + (3 * CCHMAXPATH)] = {0};
  327.         ULONG  cbBuffer   = sizeof(fsqBuffer);        // Buffer length)
  328.         PFSQBUFFER2 pfsqBuffer = (PFSQBUFFER2)fsqBuffer;
  329.  
  330.         arc = DosQueryFSAttach(
  331.                         szName,          // logical drive of attached FS
  332.                         ulOrdinal,       // ignored for FSAIL_QUERYNAME
  333.                         FSAIL_QUERYNAME, // return data for a Drive or Device
  334.                         pfsqBuffer,      // returned data
  335.                         &cbBuffer);      // returned data length
  336.  
  337.         if (arc == NO_ERROR)
  338.         {
  339.             // The data for the last three fields in the FSQBUFFER2
  340.             // structure are stored at the offset of fsqBuffer.szName.
  341.             // Each data field following fsqBuffer.szName begins
  342.             // immediately after the previous item.
  343.             PSZ pszFSDName = (CHAR*)(&pfsqBuffer->szName) + pfsqBuffer->cbName + 1;
  344.             strcpy(pszBuf, pszFSDName);
  345.         }
  346.     }
  347.  
  348.     return (arc);
  349. }
  350.  
  351. /*
  352.  *@@ doshQueryDiskType:
  353.  *      this retrieves more information about a given drive,
  354.  *      which is stored in *pulDeviceType. According to the
  355.  *      IBM Control Program Reference, this value can be:
  356.  * <BR>     0  48 TPI low-density diskette drive
  357.  * <BR>     1  96 TPI high-density diskette drive
  358.  * <BR>     2  3.5-inch 720KB diskette drive
  359.  * <BR>     3  8-Inch single-density diskette drive
  360.  * <BR>     4  8-Inch double-density diskette drive
  361.  * <BR>     5  Fixed disk
  362.  * <BR>     6  Tape drive
  363.  * <BR>     7  Other (includes 1.44MB 3.5-inch diskette drive)
  364.  * <BR>     8  R/W optical disk
  365.  * <BR>     9  3.5-inch 4.0MB diskette drive (2.88MB formatted)
  366.  * <BR> Returns DOS error code.
  367.  *      Warning: This uses DosDevIOCtl, which has proved
  368.  *      to cause problems with some device drivers for
  369.  *      removeable disks.
  370.  */
  371.  
  372. APIRET doshQueryDiskType(ULONG  ulLogicalDrive,      // in: 1 = A:, 2 = B:, ...
  373.                         PULONG pulDeviceType)       // out: device type
  374. {
  375.     APIRET arc = ERROR_INVALID_DRIVE;
  376.  
  377.     if (ulLogicalDrive) {
  378.         #pragma pack(1)
  379.         // parameter packet
  380.         struct {
  381.             UCHAR command, drive;
  382.         } parms;
  383.         // data packet
  384.         struct {
  385.             UCHAR   aucBPB[31];         // BIOS parameter block
  386.             USHORT  usCylinders;
  387.             UCHAR   ucDeviceType;
  388.             USHORT  usDeviceAttrs;
  389.         } data;
  390.         #pragma pack()
  391.  
  392.         ULONG ulParmSize = sizeof(parms);
  393.         ULONG ulDataSize = sizeof(data);
  394.  
  395.         parms.command = 1; // read currently inserted media
  396.         parms.drive=(UCHAR)(ulLogicalDrive-1);
  397.  
  398.         arc = DosDevIOCtl((HFILE)-1,
  399.                         IOCTL_DISK,
  400.                         DSK_GETDEVICEPARAMS,
  401.                         &parms, ulParmSize, &ulParmSize,
  402.                         &data,  ulDataSize, &ulDataSize);
  403.  
  404.         if (arc == NO_ERROR)
  405.             *pulDeviceType = (ULONG)(data.ucDeviceType);
  406.     }
  407.  
  408.     return (arc);
  409. }
  410.  
  411. /*
  412.  *@@ doshIsFileOnFAT:
  413.  *      returns TRUE if pszFileName resides on
  414.  *      a FAT drive.
  415.  */
  416.  
  417. BOOL doshIsFileOnFAT(PSZ pszFileName)
  418. {
  419.     BOOL brc = FALSE;
  420.     CHAR szName[5];
  421.  
  422.     szName[0] = pszFileName[0];
  423.     szName[1] = ':';
  424.     szName[2] = '\0';
  425.  
  426.     #ifdef DEBUG_TITLECLASH
  427.         _Pmpf(("cmnIsFAT for %s -> %s", pszFileName, szName));
  428.     #endif
  429.     {
  430.         ULONG  ulOrdinal       = 0;     // ordinal of entry in name list
  431.         // PBYTE  pszFSDName      = NULL;  // pointer to FS name
  432.         // PBYTE  prgFSAData      = NULL;  // pointer to FS data
  433.         APIRET rc              = NO_ERROR; // return code
  434.         BYTE   fsqBuffer[sizeof(FSQBUFFER2) + (3 * CCHMAXPATH)] = {0};
  435.         ULONG  cbBuffer   = sizeof(fsqBuffer);        // Buffer length)
  436.         PFSQBUFFER2 pfsqBuffer = (PFSQBUFFER2)fsqBuffer;
  437.  
  438.         rc = DosQueryFSAttach(
  439.                         szName,          // logical drive of attached FS
  440.                         ulOrdinal,       // ignored for FSAIL_QUERYNAME
  441.                         FSAIL_QUERYNAME, // return data for a Drive or Device
  442.                         pfsqBuffer,      // returned data
  443.                         &cbBuffer);      // returned data length
  444.  
  445.         if (rc == NO_ERROR)
  446.         {
  447.             // The data for the last three fields in the FSQBUFFER2
  448.             // structure are stored at the offset of fsqBuffer.szName.
  449.             // Each data field following fsqBuffer.szName begins
  450.             // immediately after the previous item.
  451.             PSZ pszFSDName = (PSZ)&(pfsqBuffer->szName) + pfsqBuffer->cbName + 1;
  452.             #ifdef DEBUG_TITLECLASH
  453.                 _Pmpf(("  cmnIsFAT -> File system: %s", pszFSDName));
  454.             #endif
  455.             if (strncmp(pszFSDName, "FAT", 3) == 0)
  456.                 brc = TRUE;
  457.         }
  458.     }
  459.     return (brc);
  460. }
  461.  
  462. /*
  463.  *@@ doshIsValidFileName:
  464.  *      this returns NO_ERROR only if pszFile is a valid file name.
  465.  *      This may include a full path.
  466.  *      If a drive letter is specified, this checks for whether
  467.  *      that drive is a FAT drive and adjust the checks accordingly,
  468.  *      i.e. 8+3 syntax and more invalid characters.
  469.  *      If no drive letter is specified, this check is performed
  470.  *      for the current drive.
  471.  *      Note: this performs syntactic checks only. This does not
  472.  *      check for whether the specified path components exist.
  473.  *      However, it _is_ checked for whether the given drive
  474.  *      exists.
  475.  *      If an error is found, the corresponding DOS error code
  476.  *      is returned:
  477.  * <BR>     ERROR_INVALID_DRIVE
  478.  * <BR>     ERROR_FILENAME_EXCED_RANGE  (on FAT: no 8+3 filename)
  479.  * <BR>     ERROR_INVALID_NAME          (invalid character)
  480.  */
  481.  
  482. APIRET doshIsValidFileName(PSZ pszFile)
  483. {
  484.     CHAR    szPath[CCHMAXPATH+4] = " :";
  485.     CHAR    szComponent[CCHMAXPATH];
  486.     PSZ     p1, p2;
  487.     BOOL    fIsFAT = FALSE;
  488.     PSZ     pszInvalid;
  489.  
  490.     // check drive first
  491.     if (*(pszFile + 1) == ':')
  492.     {
  493.         CHAR cDrive = toupper(*pszFile);
  494.         // drive specified:
  495.         strcpy(szPath, pszFile);
  496.         szPath[0] = toupper(*pszFile);
  497.         if (doshQueryDiskFree(cDrive - 'A' + 1) == -1)
  498.             return (ERROR_INVALID_DRIVE);
  499.     }
  500.     else
  501.     {
  502.         // no drive specified: take current
  503.         ULONG   ulDriveNum = 0,
  504.                 ulDriveMap = 0;
  505.         DosQueryCurrentDisk(&ulDriveNum, &ulDriveMap);
  506.         szPath[0] = ((UCHAR)ulDriveNum) + 'A' - 1;
  507.         szPath[1] = ':';
  508.         strcpy(&szPath[2], pszFile);
  509.     }
  510.  
  511.     fIsFAT = doshIsFileOnFAT(szPath);
  512.  
  513.     pszInvalid = (fIsFAT)
  514.                               ? "<>|+=:;,\"/[] "  // invalid characters in FAT
  515.                               : "<>|:\"/";        // invalid characters in IFS's
  516.  
  517.     // now separate path components
  518.     p1 = &szPath[2];       // advance past ':'
  519.  
  520.     do {
  521.  
  522.         if (*p1 == '\\')
  523.             p1++;
  524.  
  525.         p2 = strchr(p1, '\\');
  526.         if (p2 == NULL)
  527.             p2 = p1 + strlen(p1);
  528.  
  529.         if (p1 != p2)
  530.         {
  531.             LONG    lDotOfs = -1,
  532.                     lAfterDot = -1;
  533.             ULONG   cbFile,
  534.                     ul;
  535.             PSZ     pSource = szComponent;
  536.  
  537.             strncpy(szComponent, p1, p2-p1);
  538.             szComponent[p2-p1] = 0;
  539.             cbFile = strlen(szComponent);
  540.  
  541.             // now check each path component
  542.             for (ul = 0; ul < cbFile; ul++)
  543.             {
  544.                 if (fIsFAT)
  545.                 {
  546.                     // on FAT: only 8 characters allowed before dot
  547.                     if (*pSource == '.')
  548.                     {
  549.                         lDotOfs = ul;
  550.                         lAfterDot = 0;
  551.                         if (ul > 7)
  552.                             return (ERROR_FILENAME_EXCED_RANGE);
  553.                     }
  554.                 }
  555.                 // and check for invalid characters
  556.                 if (strchr(pszInvalid, *pSource) != NULL)
  557.                     return (ERROR_INVALID_NAME);
  558.  
  559.                 pSource++;
  560.  
  561.                 // on FAT, allow only three chars after dot
  562.                 if (fIsFAT)
  563.                     if (lAfterDot != -1)
  564.                     {
  565.                         lAfterDot++;
  566.                         if (lAfterDot > 3)
  567.                             return(ERROR_FILENAME_EXCED_RANGE);
  568.                     }
  569.             }
  570.  
  571.             // we are still missing the case of a FAT file
  572.             // name without extension; if so, check whether
  573.             // the file stem is <= 8 chars
  574.             if (fIsFAT)
  575.                 if (lDotOfs == -1)  // dot not found:
  576.                     if (cbFile > 8)
  577.                         return (ERROR_FILENAME_EXCED_RANGE);
  578.         }
  579.  
  580.         // go for next component
  581.         p1 = p2+1;
  582.     } while (*p2);
  583.  
  584.     return (NO_ERROR);
  585. }
  586.  
  587. /*
  588.  *@@ doshMakeRealName:
  589.  *      this copies pszSource to pszTarget, replacing
  590.  *      all characters which are not supported by file
  591.  *      systems with cReplace.
  592.  *      pszTarget must be at least the same size as pszSource.
  593.  *      If (fIsFAT), the file name will be made FAT-compliant (8+3).
  594.  *      Returns TRUE if characters were replaced.
  595.  */
  596.  
  597. BOOL doshMakeRealName(PSZ pszTarget,    // out: new real name
  598.                       PSZ pszSource,    // in: filename to translate
  599.                       CHAR cReplace,    // in: replacement char for invalid
  600.                                         //     characters (e.g. '!')
  601.                       BOOL fIsFAT)      // in: make-FAT-compatible flag
  602. {
  603.     ULONG ul,
  604.           cbSource = strlen(pszSource);
  605.     LONG  lDotOfs = -1,
  606.           lAfterDot = -1;
  607.     BOOL  brc = FALSE;
  608.     PSZ   pSource = pszSource,
  609.           pTarget = pszTarget,
  610.           pszInvalid = (fIsFAT)
  611.                             ? "<>|+=:;,\"/\\[] "  // invalid characters in FAT
  612.                             : "<>|:\"/\\"; // invalid characters in IFS's
  613.  
  614.     for (ul = 0; ul < cbSource; ul++)
  615.     {
  616.         if (fIsFAT) {
  617.             // on FAT: truncate filename if neccessary
  618.             if (*pSource == '.')
  619.             {
  620.                 lDotOfs = ul;
  621.                 lAfterDot = 0;
  622.                 if (ul > 7) {
  623.                     // only 8 characters allowed before dot,
  624.                     // so set target ptr to dot pos
  625.                     pTarget = pszTarget+8;
  626.                 }
  627.             }
  628.         }
  629.         // and replace invalid characters
  630.         if (strchr(pszInvalid, *pSource) == NULL)
  631.             *pTarget = *pSource;
  632.         else {
  633.             *pTarget = cReplace;
  634.             brc = TRUE;
  635.         }
  636.         pTarget++;
  637.         pSource++;
  638.  
  639.         // on FAT, allow only three chars after dot
  640.         if (fIsFAT)
  641.             if (lAfterDot != -1) {
  642.                 lAfterDot++;
  643.                 if (lAfterDot > 3)
  644.                     break;
  645.             }
  646.     }
  647.     *pTarget = '\0';
  648.     #ifdef DEBUG_TITLECLASH
  649.         _Pmpf(("      strMakeRealName: returning %s", pszTarget));
  650.     #endif
  651.  
  652.     if (fIsFAT)
  653.     {
  654.         // we are still missing the case of a FAT file
  655.         // name without extension; if so, check whether
  656.         // the file stem is <= 8 chars
  657.         if (lDotOfs == -1)  // dot not found:
  658.             if (cbSource > 8)
  659.                 *(pszTarget+8) = 0; // truncate
  660.  
  661.         // convert to upper case
  662.         strupr(pszTarget);
  663.     }
  664.  
  665.     return (brc);
  666. }
  667.  
  668. /*
  669.  *@@ doshQueryFileSize:
  670.  *      returns the size of an already opened file.
  671.  */
  672.  
  673. ULONG doshQueryFileSize(HFILE hFile)
  674. {
  675.     FILESTATUS3 fs3;
  676.     if (DosQueryFileInfo(hFile, FIL_STANDARD, &fs3, sizeof(fs3)))
  677.         return (0);
  678.     else
  679.         return (fs3.cbFile);
  680. }
  681.  
  682. /*
  683.  *@@ doshQueryPathSize:
  684.  *      returns the size of any file,
  685.  *      or 0 if the file could not be
  686.  *      found.
  687.  */
  688.  
  689. ULONG doshQueryPathSize(PSZ pszFile)
  690. {
  691.     FILESTATUS3 fs3;
  692.     if (DosQueryPathInfo(pszFile, FIL_STANDARD, &fs3, sizeof(fs3)))
  693.         return (0);
  694.     else
  695.         return (fs3.cbFile);
  696. }
  697.  
  698. /*
  699.  *@@ doshQueryDirExist:
  700.  *      returns TRUE if the given directory
  701.  *      exists.
  702.  */
  703.  
  704. BOOL doshQueryDirExist(PSZ pszDir)
  705. {
  706.     FILESTATUS3 fs3;
  707.     APIRET arc = DosQueryPathInfo(pszDir, FIL_STANDARD, &fs3, sizeof(fs3));
  708.     if (arc == NO_ERROR)
  709.         // file found:
  710.         return ((fs3.attrFile & FILE_DIRECTORY) != 0);
  711.     else
  712.         return FALSE;
  713. }
  714.  
  715. /*
  716.  *@@ doshCreatePath:
  717.  *      this creates the specified directory.
  718.  *      As opposed to DosCreateDir, this
  719.  *      function can create several directories
  720.  *      at the same time, if the parent
  721.  *      directories do not exist yet.
  722.  */
  723.  
  724. APIRET doshCreatePath(PSZ pszPath)
  725. {
  726.     APIRET  arc0 = NO_ERROR;
  727.     CHAR    path[CCHMAXPATH];
  728.     CHAR    *cp, c;
  729.     ULONG   cbPath;
  730.  
  731.     strcpy(path, pszPath);
  732.     cbPath = strlen(path);
  733.  
  734.     if (path[cbPath] != '\\')
  735.     {
  736.         path[cbPath] = '\\';
  737.         path[cbPath+1] = 0;
  738.     }
  739.  
  740.     cp = path;
  741.     // advance past the drive letter only if we have one
  742.     if (*(cp+1) == ':')
  743.         cp += 3;
  744.  
  745.     // Now, make the directories
  746.     while (*cp != 0)
  747.     {
  748.         if (*cp == '\\')
  749.         {
  750.             c = *cp;
  751.             *cp = 0;
  752.             if (!doshQueryDirExist(path))
  753.             {
  754.                 APIRET arc = DosCreateDir(path, 0);
  755.                 if (arc != NO_ERROR)
  756.                 {
  757.                     arc0 = arc;
  758.                     break;
  759.                 }
  760.             }
  761.             *cp = c;
  762.         }
  763.         cp++;
  764.     }
  765.     return (arc0);
  766. }
  767.  
  768. /*
  769.  *@@ doshSetCurrentDir:
  770.  *      sets the current working directory
  771.  *      to the given path.
  772.  *      As opposed to DosSetCurrentDir, this
  773.  *      one will change the current drive
  774.  *      also, if one is specified.
  775.  */
  776.  
  777. APIRET doshSetCurrentDir(PSZ pszDir)
  778. {
  779.     if (pszDir)
  780.     {
  781.         if (*pszDir != 0)
  782.             if (*(pszDir+1) == ':')
  783.             {
  784.                 // drive given:
  785.                 CHAR    cDrive = toupper(*(pszDir));
  786.                 APIRET  arc;
  787.                 // change drive
  788.                 arc = DosSetDefaultDisk( (ULONG)(cDrive - 'A' + 1) );
  789.                         // 1 = A:, 2 = B:, ...
  790.                 if (arc != NO_ERROR)
  791.                     return (arc);
  792.             }
  793.  
  794.         return (DosSetCurrentDir(pszDir));
  795.     }
  796.     return (ERROR_INVALID_PARAMETER);
  797. }
  798.  
  799. /*
  800.  *@@ doshReadTextFile:
  801.  *      reads a text file from disk, allocates memory
  802.  *      via malloc() and returns a pointer to this
  803.  *      buffer (or NULL upon errors). Specify in
  804.  *      ulExtraMemory how much extra memory (in addition
  805.  *      to the file's size) should be allocated to
  806.  *      allow for text manipulation.
  807.  *      Returns NULL if an error occured.
  808.  */
  809.  
  810. PSZ doshReadTextFile(PSZ pszFile, ULONG ulExtraMemory)
  811. {
  812.     ULONG   ulSize,
  813.             ulBytesRead = 0,
  814.             ulAction, ulLocal;
  815.     HFILE   hFile;
  816.     PSZ     pszContent = NULL;
  817.     APIRET rc = DosOpen(pszFile,
  818.                  &hFile,
  819.                  &ulAction,                      // action taken
  820.                  5000L,                          // primary allocation size
  821.                  FILE_ARCHIVED | FILE_NORMAL,    // file attribute
  822.                  /* OPEN_ACTION_CREATE_IF_NEW | */
  823.                  OPEN_ACTION_OPEN_IF_EXISTS,     // open flags
  824.                  OPEN_FLAGS_NOINHERIT |
  825.                  OPEN_SHARE_DENYNONE  |
  826.                  OPEN_ACCESS_READONLY,               // read-write mode
  827.                  NULL);                          // no EAs
  828.  
  829.     if (rc == NO_ERROR) {
  830.         ulSize = doshQueryFileSize(hFile);
  831.         pszContent = (PSZ)malloc(ulSize+10+ulExtraMemory);
  832.         rc = DosSetFilePtr(hFile,
  833.                             0L,
  834.                             FILE_BEGIN,
  835.                             &ulLocal);
  836.         rc = DosRead(hFile,
  837.                       pszContent,
  838.                       ulSize,
  839.                       &ulBytesRead);
  840.         DosClose(hFile);
  841.         *(pszContent+ulBytesRead) = '\0';
  842.     }
  843.     return (pszContent);
  844. }
  845.  
  846. /*
  847.  *@@ doshCreateBackupFileName:
  848.  *      creates a valid backup filename of pszExisting
  849.  *      with a numerical file name extension which does
  850.  *      not exist in the directory where pszExisting
  851.  *      resides.
  852.  *      Returns a PSZ to a new buffer which was allocated
  853.  *      using malloc().
  854.  *      <P><B>Example:</B> returns "C:\CONFIG.002" for input
  855.  *      "C:\CONFIG.SYS" if "C:\CONFIG.001" already exists.
  856.  */
  857.  
  858. PSZ doshCreateBackupFileName(PSZ pszExisting)
  859. {
  860.     // CHAR    szTemp[CCHMAXPATH];
  861.     PSZ     pszNew = strdup(pszExisting),
  862.             pszLastDot = strrchr(pszNew, '.');
  863.     ULONG   ulCount = 1;
  864.     CHAR    szCount[5];
  865.     do {
  866.         sprintf(szCount, ".%03d", ulCount);
  867.         strcpy(pszLastDot, szCount);
  868.         ulCount++;
  869.     } while (doshQueryPathSize(pszNew) != 0);
  870.     return (pszNew);
  871. }
  872.  
  873. /*
  874.  *@@ doshWriteTextFile:
  875.  *      writes a text file to disk; pszFile must contain the
  876.  *      whole path and filename.
  877.  *      An existing file will be backed up if (fBackup == TRUE),
  878.  *      using doshCreateBackupFileName; otherwise the file
  879.  *      will be overwritten.
  880.  *      Returns the number of bytes written or 0 upon errors.
  881.  */
  882.  
  883. ULONG doshWriteTextFile(PSZ pszFile,        // in: file name
  884.                         PSZ pszContent,     // in: text to write
  885.                         BOOL fBackup)       // in: create-backup flag
  886. {
  887.     ULONG   ulWritten = 0,
  888.             ulAction, ulLocal;
  889.     HFILE   hFile;
  890.     APIRET rc;
  891.  
  892.     if (fBackup) {
  893.         PSZ     pszBackup = doshCreateBackupFileName(pszFile);
  894.         DosCopy(pszFile, pszBackup, DCPY_EXISTING);
  895.         free(pszBackup);
  896.     }
  897.  
  898.     rc = DosOpen(pszFile,
  899.                  &hFile,
  900.                  &ulAction,                      // action taken
  901.                  5000L,                          // primary allocation size
  902.                  FILE_ARCHIVED | FILE_NORMAL,    // file attribute
  903.                  OPEN_ACTION_CREATE_IF_NEW
  904.                  | OPEN_ACTION_REPLACE_IF_EXISTS,  // open flags
  905.                  OPEN_FLAGS_NOINHERIT
  906.                  | OPEN_FLAGS_WRITE_THROUGH      // write immediately w/out cache
  907.                  | OPEN_FLAGS_NO_CACHE           // do not store in cache
  908.                  | OPEN_FLAGS_SEQUENTIAL         // sequential, not random access
  909.                  | OPEN_SHARE_DENYNONE
  910.                  | OPEN_ACCESS_WRITEONLY,        // read-write mode
  911.                  NULL);                          // no EAs
  912.  
  913.     if (rc == NO_ERROR)
  914.     {
  915.         ULONG ulSize = strlen(pszContent);      // exclude 0 byte
  916.  
  917.         rc = DosSetFilePtr(hFile,
  918.                             0L,
  919.                             FILE_BEGIN,
  920.                             &ulLocal);
  921.         if (rc == NO_ERROR) {
  922.             rc = DosWrite(hFile,
  923.                           pszContent,
  924.                           ulSize,
  925.                           &ulWritten);
  926.             if (rc == NO_ERROR)
  927.                 rc = DosSetFileSize(hFile, ulSize);
  928.         }
  929.  
  930.         DosClose(hFile);
  931.  
  932.         if (rc != NO_ERROR)
  933.             ulWritten = 0;
  934.     }
  935.  
  936.     return (ulWritten);
  937. }
  938.  
  939. /*
  940.  *@@ doshOpenLogFile:
  941.  *      this opens a log file in the root directory of
  942.  *      the boot drive; it is titled pszFilename, and
  943.  *      the file handle is returned.
  944.  */
  945.  
  946. HFILE doshOpenLogFile(PSZ pszFileName)
  947. {
  948.     APIRET  rc;
  949.     CHAR    szFileName[CCHMAXPATH];
  950.     HFILE   hfLog;
  951.     ULONG   ulAction;
  952.     ULONG   ibActual;
  953.  
  954.     sprintf(szFileName, "%c:\\%s", doshQueryBootDrive(), pszFileName);
  955.     rc = DosOpen(szFileName, &hfLog, &ulAction, 0,
  956.                  FILE_NORMAL,
  957.                  OPEN_ACTION_CREATE_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS,
  958.                  OPEN_ACCESS_WRITEONLY | OPEN_SHARE_DENYWRITE,
  959.                  (PEAOP2)NULL);
  960.     if (rc == NO_ERROR) {
  961.         DosSetFilePtr(hfLog, 0, FILE_END, &ibActual);
  962.         return (hfLog);
  963.     } else
  964.         return (0);
  965. }
  966.  
  967. /*
  968.  * doshWriteToLogFile
  969.  *      writes a string to a log file, adding a
  970.  *      leading timestamp.
  971.  */
  972.  
  973. APIRET doshWriteToLogFile(HFILE hfLog, PSZ psz)
  974. {
  975.     if (hfLog) {
  976.         DATETIME dt;
  977.         CHAR szTemp[2000];
  978.         ULONG   cbWritten;
  979.         DosGetDateTime(&dt);
  980.         sprintf(szTemp, "Time: %02d:%02d:%02d %s",
  981.             dt.hours, dt.minutes, dt.seconds,
  982.             psz);
  983.         return (DosWrite(hfLog, psz, strlen(psz), &cbWritten));
  984.     }
  985.     else return (ERROR_INVALID_HANDLE);
  986. }
  987.  
  988. /*
  989.  *@@ doshQuickStartSession:
  990.  *      shortcut to DosStartSession w/out having to deal
  991.  *      with all the parameters. This one starts a session
  992.  *      as a child session and can optionally wait for the
  993.  *      session to end (using a termination queue), if
  994.  *      Wait == TRUE; otherwise, this function returns
  995.  *      immediately.
  996.  *      Returns the error code of DosStartSession.
  997.  */
  998.  
  999. APIRET doshQuickStartSession(PSZ pszPath,     // in: program to start
  1000.                              PSZ pszParams,   // in: parameters for program
  1001.                              BOOL Visible,    // in: show program?
  1002.                              BOOL Wait,       // in: wait for termination?
  1003.                              PULONG pulSID,   // out: session ID
  1004.                              PPID ppid)       // out: process ID
  1005. {
  1006.     APIRET rc;
  1007.     REQUESTDATA rqdata;
  1008.     ULONG DataLength; PULONG DataAddress; BYTE elpri;
  1009.     int er;
  1010.     // Queue Stuff
  1011.     HQUEUE hq;
  1012.     PID qpid=0;
  1013.     STARTDATA   SData;
  1014.     CHAR        szObjBuf[CCHMAXPATH];
  1015.  
  1016.     if (Wait) {
  1017.         if (    (er=DosCreateQueue(&hq, QUE_FIFO|QUE_CONVERT_ADDRESS, "\\queues\\kfgstart.que"))
  1018.              != NO_ERROR)
  1019.         {
  1020.             char str[80];
  1021.             sprintf(str, "Create %ld\n", er);
  1022.         }
  1023.  
  1024.         if ((er=DosOpenQueue(&qpid, &hq, "\\queues\\kfgstart.que")) != NO_ERROR)
  1025.         {
  1026.             char str[80];
  1027.             sprintf(str, "Open %ld\n", er);
  1028.         }
  1029.     }
  1030.  
  1031.     SData.Length  = sizeof(STARTDATA);
  1032.     SData.Related = SSF_RELATED_CHILD; //INDEPENDENT;
  1033.     SData.FgBg    = SSF_FGBG_FORE;
  1034.     SData.TraceOpt = SSF_TRACEOPT_NONE;
  1035.  
  1036.     SData.PgmTitle = pszPath;       // title for window
  1037.     SData.PgmName = pszPath;
  1038.     SData.PgmInputs = pszParams;
  1039.  
  1040.     SData.TermQ = (Wait) ? "\\queues\\kfgstart.que" : NULL;
  1041.     SData.Environment = 0;
  1042.     SData.InheritOpt = SSF_INHERTOPT_SHELL;
  1043.     SData.SessionType = SSF_TYPE_DEFAULT;
  1044.     SData.IconFile = 0;
  1045.     SData.PgmHandle = 0;
  1046.  
  1047.     SData.PgmControl = ((Visible) ? SSF_CONTROL_VISIBLE : SSF_CONTROL_INVISIBLE);
  1048.     SData.InitXPos  = 30;
  1049.     SData.InitYPos  = 40;
  1050.     SData.InitXSize = 200;
  1051.     SData.InitYSize = 140;
  1052.     SData.Reserved = 0;
  1053.     SData.ObjectBuffer  = (CHAR*)&szObjBuf;
  1054.     SData.ObjectBuffLen = (ULONG)sizeof(szObjBuf);
  1055.  
  1056.     rc = DosStartSession(&SData, pulSID, ppid);
  1057.  
  1058.     if (rc == NO_ERROR) {
  1059.         if (Wait) {
  1060.             rqdata.pid=qpid;
  1061.             DosReadQueue(hq, &rqdata, &DataLength, (PVOID*)&DataAddress, 0, 0, &elpri, 0);
  1062.             DosCloseQueue(hq);
  1063.         }
  1064.     }
  1065.     return (rc);
  1066. }
  1067.  
  1068. /*
  1069.  *@@ doshSetPathAttr:
  1070.  *      sets the file attributes of pszFile,
  1071.  *      which can be fully qualified.
  1072.  *      fAttr can be:
  1073.  * <BR>         FILE_ARCHIVED
  1074.  * <BR>         FILE_READONLY
  1075.  * <BR>         FILE_SYSTEM
  1076.  * <BR>         FILE_HIDDEN
  1077.  * <BR> Note that this sets all the given
  1078.  *      attributes; the existing attributes
  1079.  *      are lost.
  1080.  */
  1081.  
  1082. APIRET doshSetPathAttr(PSZ pszFile, ULONG fAttr)
  1083. {
  1084.     FILESTATUS3 fs3;
  1085.     APIRET rc = DosQueryPathInfo(pszFile,
  1086.             FIL_STANDARD,
  1087.             &fs3,
  1088.             sizeof(fs3));
  1089.  
  1090.     if (rc == NO_ERROR) {
  1091.         fs3.attrFile = fAttr;
  1092.         rc = DosSetPathInfo(pszFile,
  1093.                 FIL_STANDARD,
  1094.                 &fs3,
  1095.                 sizeof(fs3),
  1096.                 DSPI_WRTTHRU);
  1097.     }
  1098.     return (rc);
  1099. }
  1100.  
  1101.  
  1102.