/*
 *  copy.c - Copy routine for wInstall
 *
 *  notes:
 *	we now use the LZCopy stuff so COMPRESS is NOT defined
 *	we now set the crit error handler ourselves so CHECKFLOPPY is
 *	NOT defined
 */

#include <dos.h>
#include <string.h>

#include "lobotomy.h"

// ...and lets even remove some more brain cells...

#define NOWINSTYLES       //- WS_*, CS_*, ES_*, LBS_*, SBS_*, CBS_*
#define NOSHOWWINDOW      //- SW_*
#define NOATOM            //- Atom Manager routines
#define NOCOLOR           //- Screen colors
#define NOGDI             //- All GDI defines and routines
#define NOMINMAX          //- Macros min(a,b) and max(a,b)
#define NOOPENFILE        //- OpenFile(), OemToAnsi, AnsiToOem, and OF_*
#define NOTEXTMETRIC      //- typedef TEXTMETRIC and associated routines
#define NOWINOFFSETS      //- GWL_*, GCL_*, associated routines

#include <windows.h>

#include "sulib.h"
#include "..\install.h"
#include "ws.h"
#include "progdde.h"
#include "gauge.h"

char szDisks[] = "disks";

BOOL NEAR GetDiskPath(char cDisk, PSTR szPath);
WORD FAR PASCAL wsCopyStatus(int msg, int n, LPSTR szFile);
BOOL fnMystrstr(char *szSrcStr, char *szSearchStr);

/*
 *  global vars used by DosCopy
 */
static LPSTR    lpBuf = NULL;   // copy buffer
static int      iBuf = 0;       // usage count
static WORD     nBufSize;
static char	cDisk;
static LPSTR	szEdit;
extern PSTR	pErrMsg;

#define MAXBUF  (60 * 1024)     // size of default copy buffer

void NEAR PASCAL AllocCopyBuf();
void NEAR PASCAL FreeCopyBuf();

void FAR PASCAL fartonear(LPSTR dst, LPSTR src)
{
	while (*src)
		*dst++ = *src++;

	*dst = 0;
}

/*  WORD FileCopy (szSource, szDir, fpfnCopy, WORD f)
 *
 *  This function will copy a group of files to a single destination
 *
 *  ENTRY:
 *
 *  szSourc      : pointer to a SETUP.INF section
 *  szDest       : pointer to a string containing the target DIR
 *  fpfnCopy     : callback function used to notify called of copy status
 *  fCopy        : flags
 *
 *      FC_SECTION      - szSource is a section name
 *      FC_LIST         - szSource is a pointer to a list, each item \0
 *                        terminated and the whole list \0\0 terminated
 *      FC_FILE         - szSource is a file name.
 *      FC_QUALIFIED    - szSource is a fully qualified file name.
 *      FC_DEST_QUALIFIED - szDir is fully qualified. Don't expand this.
 *
 *  NOTES:
 *      if szSource points to a string of the form '#name' the section
 *      named by 'name' will be used as the source files
 *
 *      the first field of each line in the secion is used as the name of the
 *      source file.  A file name has the following form:
 *
 *          #:name
 *
 *          #       - Disk number containing file 1-9,A-Z
 *          name    - name of the file, may be a wild card expression
 *
 *  Format for copy status function
 *
 *  BOOL FAR PASCAL CopyStatus(int msg, int n, PSTR szFile)
 *
 *      msg:
 *          COPY_ERROR          error occured while copying file(s)
 *                              n      is the DOS error number
 *                              szFile is the file that got the error
 *                              return: TRUE ok, FALSE abort copy
 *
 *          COPY_STATUS         Called each time a new file is copied
 *                              n      is the percent done
 *                              szFile is the file being copied
 *                              return: TRUE ok, FALSE abort copy
 *
 *          COPY_INSERTDISK     Please tell the user to insert a disk
 *                              n      is the disk needed ('1' - '9')
 *                              return: TRUE try again, FALSE abort copy
 *
 *          COPY_QUERYCOPY      Should this file be copied?
 *                              n      line index in SETUP.INF section (0 based)
 *                              szFile is the line from section
 *                              return: TRUE copy it, FALSE dont copy
 *
 *          COPY_START          Sent before any files are copied
 *
 *          COPY_END            Sent after all files have been copied
 *                              n   is dos error if copy failed
 *
 *
 *  EXIT: returns TRUE if successful, FALSE if failure.
 *
 */

WORD FAR PASCAL FileCopy (LPSTR szSource, PSTR szDir, FPFNCOPY fpfnCopy, WORD fCopy)
{
    int       err = ERROR_OK;
    char      szFile[MAXPATHLEN];
    char      szPath[MAXPATHLEN];
    char      szLogSrc[MAXPATHLEN];
    char      szSrcBase[15];
    char      szSrc[MAXPATHLEN];
    char      szErrFile[MAXPATHLEN];
    LPSTR far *pFileList;
    LPSTR far *pFileListBegin;
    LPSTR     pFile;
    LPSTR     pFileBegin;
    BOOL      f;
    BOOL      fDoCopy;
    int       n = 0;
    int       nDisk;
    char      cDisk;
    int       cntFiles = 0;
    PINF      pinf;

    #define CALLBACK(msg,n,pFile) \
        (fpfnCopy ? ((*fpfnCopy)((WORD)(msg),(int)(n),(LPSTR)(pFile))) : FC_IGNORE)

    if (!szSource || !*szSource || !szDir || !*szDir)
        return FALSE;

#ifndef COMPRESS
    AllocCopyBuf();
#endif

    /*
     *  fix up the drive in the destination
     */

    if ( fCopy & FC_DEST_QUALIFIED )
       lstrcpy(szPath,szDir);
    else
       ExpandFileName(szDir,szPath);

    fCopy &= ~FC_DEST_QUALIFIED;

    if (szSource[0] == '#' && fCopy == FC_FILE)
    {
        fCopy = FC_SECTION;
        ++szSource;
    }

    switch (fCopy)
    {
        case FC_LSTPTR:
            pFileList = pFileListBegin = (LPSTR far *)szSource;
            pFileBegin = *pFileList;

            while ( pFileList[n] ) {
               if ( *pFileList[n] )
                  ++cntFiles;
               ++n;
            }

            break;

        case FC_SECTION:
	    {
	    char buf[40];

	    fartonear(buf, szSource);
            szSource = infFindSection(NULL,buf);
            if (szSource == NULL)
                goto exit;

	    fCopy = FC_LIST;
	    }
            // fall through to FC_LIST

        case FC_LIST:
            pFileBegin = szSource;
            cntFiles = infLineCount(szSource);
            break;

        case FC_FILE:
        case FC_QUALIFIED:
        default:
            pFileBegin = szSource;
            cntFiles = 1;
    }

    /*
     * Does the destination directory exist? if not create it.
     */
    if (!DosValidDir(szPath)) {

        err = DosMkDir(szPath);

	// oh no! this is bad

        if (err != ERROR_OK) {
            CALLBACK(COPY_ERROR,err,szPath);
            goto exit;
        }
    }

    /*
     *  walk all files in the list and call DosCopy ....
     *
     *  NOTES:
     *      we must walk file list sorted by disk number.
     *      we should use the disk that is currently inserted.
     *      we should do a find first/find next on the files????
     *      we need to check for errors.
     *      we need to ask the user to insert disk in drive.
     *
     */
    CALLBACK(COPY_START,0,NULL);

    for (nDisk = 1; cntFiles > 0; nDisk++) {

        cDisk      = CHDISK(nDisk);
        pFileList  = pFileListBegin;
        pFile      = pFileBegin;
        n          = 0;

        while (pFile) {

            /*
             *  should we copy this file?
             *  copy the files in disk order.
             */
            fDoCopy = pFile[1] == ':' && cDisk == UP_CASE(pFile[0]) ||
                      pFile[1] != ':' && nDisk == 1 && *pFile ||
                      fCopy == FC_QUALIFIED;

            if (fDoCopy)
                cntFiles--;         // done with a file. decrement count.

            if (fDoCopy && CALLBACK(COPY_QUERYCOPY,n,pFile)) {

                if (CALLBACK(COPY_STATUS, 0, pFile) == FC_ABORT) {
		    err = ERROR_NOFILES;
                    goto exit;
		}

		// now we convert logical dest into a physical (unless FC_QUALIFIED)

                infParseField(pFile, 1, szLogSrc);	 // logical source
                if ( fCopy != FC_QUALIFIED )
                   ExpandFileName(szLogSrc, szSrc); // full physical source
                else
                   lstrcpy(szSrc,szLogSrc);

tryagain:   
                // Call low level copy command

                err = DosCopy(szSrc, szPath);
      
                if (err != ERROR_OK) {

		    lstrcpy(szSrcBase, FileName(szSrc)); // save base name

                    if (err == ERROR_FILENOTFOUND || err == ERROR_PATHNOTFOUND) {

                        // isolate the path

                        StripPathName(szSrc);

		   	// now try to get a new path in szSrc

                   	switch (CALLBACK(COPY_INSERTDISK, szLogSrc[0], szSrc)) {
	           	case FC_RETRY:
		   		catpath(szSrc, szSrcBase);	// add the file back on
		   		goto tryagain;			// and try again...

                   	case FC_ABORT:
                   		goto exit;

	           	case FC_IGNORE:
        	        	break;
                   	}

                    }

		    // ERROR situation
		    //
		    // this may be a real error or something like
		    // a share violation on a network.

		    ExpandFileName(szLogSrc, szSrc);	// full physical source

		    // if it is a write error report the destination file
		    // otherwise report with the source file

		    switch (err) {
		    case ERROR_WRITE:
		    	lstrcpy(szErrFile, szPath);
			catpath(szErrFile, szSrcBase);
		    	break;

		    default:
		    	lstrcpy(szErrFile, szSrc);
		    }

                    switch (CALLBACK(COPY_ERROR, err, szErrFile)) {

                    case FC_RETRY:
                            goto tryagain;
    
                    case FC_ABORT:
                            goto exit;

                    case FC_IGNORE:
                            break;
                    }
                }

                if (CALLBACK(COPY_STATUS,100,pFile) == FC_ABORT) {
                    err = !ERROR_OK;
                    goto exit;
                }
            }

            /*
             * Move on to next file in the list
             */
            n++;
            if (fCopy == FC_LSTPTR)
                pFile = *(++pFileList);
	         else if (fCopy == FC_LIST)
                pFile = infNextLine(pFile);
            else
                pFile = NULL;
        }
    }

    err = ERROR_OK;

exit:
    CALLBACK(COPY_END,err,NULL);
#ifndef COMPRESS
    FreeCopyBuf();
#endif
    return err;

    #undef CALLBACK
}

#ifndef COMPRESS

/*  AllocCopyBuf()
 *
 *  allocate a buffer for DosCopy to use
 *
 */
void NEAR PASCAL AllocCopyBuf()
{
    if (iBuf++ == 0)
    {
        nBufSize = MAXBUF;
        for(;;)
        {
            lpBuf = FALLOC(nBufSize);
            if (lpBuf || nBufSize == 1)
                break;
            nBufSize /= 2;
        }
        if (lpBuf == NULL)
            iBuf--;
    }
}

/*  FreeCopyBuf()
 *
 *  free copy buffer, if its use count is zero
 *
 */
void NEAR PASCAL FreeCopyBuf()
{
    if (iBuf > 0 && --iBuf == 0 && lpBuf)
    {
        FFREE(lpBuf);
    }
}
#endif

PSTR GetExtension(PSTR szFile)
{
	PSTR ptr;

	for (ptr = szFile; *ptr && *ptr != '.'; ptr++);

	if (*ptr != '.')
		return NULL;
	else
		return ptr+1;

}

BOOL GetCompressedName(PSTR szComp, PSTR szSrc)
{
	PSTR ptr;

	lstrcpy(szComp, szSrc);

	ptr = GetExtension(szComp);

	if (ptr && !lstrcmpi(ptr, "sys")) {
		szComp[lstrlen(szComp)-1] = '$';
		return TRUE;
	}

	return FALSE;
}

/*  DosCopy(PSTR szSrc, PSTR szPath)
 *
 *  Copy the file specifed by szSrc to the drive and directory
 *  specifed by szPath
 *
 *  ENTRY:
 *      szSrc   - File name to copy from
 *      szPath  - directory to copy to
 *
 *  RETURNS:
 *      0 - no error, else dos error code
 *
 */
int NEAR DosCopy(PSTR szSrc, PSTR szPath)
{
    FCB         fcb;
    WORD        size;
    int         fhSrc,fhDst;
    char        szFile[MAXPATHLEN];
    char        szComp[MAXPATHLEN];
    int         f = ERROR_OK;
    unsigned    date;
    unsigned    time;
    long	l;
    BOOL	bCompressedName;
    

#ifdef DEBUG
    if (fDontCopy)
        return ERROR_OK;

    if (infGetProfileString(NULL,"setup","copy",szFile) && szFile[0] == 'f')
        return ERROR_OK;
#endif

    AllocCopyBuf();

    if (!lpBuf)
        return ERROR_NOMEMORY;

#ifdef CHECK_FLOPPY
    if (!IsDiskInDrive(szSrc[0]))
    {
        f = ERROR_FILENOTFOUND;
        goto errfree;
    }
#endif


    // allows both sy$ and sys on the disks

    if (GetCompressedName(szComp, szSrc) &&
    	DosFindFirst(&fcb, szComp, ATTR_FILES)) {

    	bCompressedName = TRUE;

    } else {

        bCompressedName = FALSE;

    	if (!DosFindFirst(&fcb, szSrc, ATTR_FILES)) {
		f = ERROR_FILENOTFOUND;
		goto errfree;
	}
    }

    /*
     * copy every file that matches the file pattern passed in.
     */
    do
    {
        /*
         * create the source file name from the source path and the file
         * name that DosFindFirst/Next found
         */
        lstrcpy(szFile,szSrc);
        StripPathName(szFile);
        catpath(szFile,fcb.szName);

        fhSrc = FOPEN(szFile);

        if (fhSrc == -1)
        {
            f = FERROR();
            goto errfree;
        }

        /* Save date of opened file */

        if (_dos_getftime(fhSrc,&date,&time))
           goto errclose1;

        /*
         * create the destination file name from the dest path and the file
         * name that DosFindFirst/Next found
         */
        lstrcpy(szFile,szPath);

	// don't support wildcards for compressed files

        if (bCompressedName)
		catpath(szFile,FileName(szSrc));
	else
	        catpath(szFile,fcb.szName);	// used name from fcb

        fhDst = FCREATE(szFile);

        if (fhDst == -1)
        {
            f = FERROR();
            goto errclose1;
        }

        while (size = FREAD(fhSrc,lpBuf,nBufSize))
        {
            if (FWRITE(fhDst,lpBuf,size) != size)
            {
                /* write error? */
                f = FERROR();
                if (f == ERROR_OK)
                    f = ERROR_WRITE;
                goto errclose;
            }
        }

        /* Restore date of written file */
        _dos_setftime(fhDst,date,time);

errclose:
        FCLOSE(fhDst);
errclose1:
        FCLOSE(fhSrc);

    }   while (f == ERROR_OK && DosFindNext(&fcb));

errfree:

    FreeCopyBuf();

    return f;
}


/* BOOL fnMystrstr(char *szSrcStr, char *szSearchStr);
 *
 * Function will return BOOL value as to weather the Search string exists
 * any where within the source string. The difference between this func
 * the C run time func is that this one is simpler and is also not case
 * sensitive.
 *
 * ENTRY: szSrcStr    - Char buffer to be searched.
 *
 *        szSearchStr - String that will be searched for.
 *
 * EXIT:  BOOL value as to weather or not string was found.
 *
 *
 * WARNING: Source and search strings MUST be null terminated.
 *          
 *
 */
BOOL fnMystrstr(szSrcStr, szSearchStr)
char       *szSrcStr;
char       *szSearchStr;
{
   unsigned      len;             // Get length of search string.

   while (szSearchStr[0] == '.' && SLASH(szSearchStr[1]))
      szSearchStr+=2;

   len = lstrlen(szSearchStr);

   while ( !ISEOL(*szSrcStr) ) {
      if ( ! strnicmp(szSrcStr,szSearchStr,len))
         return TRUE;
      ++szSrcStr;
   }
   return FALSE;

}

/*  BOOL GetDiskPath(char cDisk, szPath)
 *
 *  This function will retrive the full path name for a logical disk
 *  the code reads the [disks] section of SETUP.INF and looks for
 *  n = path where n is the disk char.  NOTE the disk '0' defaults to
 *  the root windows directory.
 *
 *  ENTRY:
 *
 *  cDisk        : what disk to find 0-9,A-Z
 *  szPath       : buffer to hold disk path
 *
 */
BOOL NEAR GetDiskPath(char cDisk, PSTR szPath)
{
    char    ach[2];
    char    szBuf[MAXPATHLEN];

    if (cDisk == '0')
    {
        /*
         * return the windows setup directory
         */
        lstrcpy(szPath,szSetupPath);
        return TRUE;
    }

    /*
     * now look in the [disks] section for a full path name
     */
    ach[0] = cDisk;
    ach[1] = 0;
    if ( !infGetProfileString(NULL,szDisks,ach,szPath) )
       return FALSE;
    infParseField(szPath,1,szPath);
    /*
     *  is the path relative? is so prepend the szDiskPath
     */
    if (szPath[0] == '.' || szPath[0] == 0)
    {
        lstrcpy(szBuf,szDiskPath);
        if (! fnMystrstr(szDiskPath,szPath) )
           catpath(szBuf,szPath);
        lstrcpy(szPath,szBuf);
    }
    return TRUE;
}


/*  BOOL FAR PASCAL ExpandFileName(PSTR szFile, PSTR szPath)
 *
 *  This function will retrive the full path name for a file
 *  it will expand, logical disk letters to pyshical ones
 *  will use current disk and directory if non specifed.
 *
 *  if the drive specifed is 0-9, it will expand the drive into a
 *  full pathname using GetDiskPath()
 *
 *  IE  0:system ==>  c:windows\system
 *      1:foo.txt     a:\foo.txt
 *
 *  ENTRY:
 *
 *  szFile       : File name to expandwhat disk to find
 *  szPath       : buffer to hold full file name
 *
 */
BOOL FAR PASCAL ExpandFileName(PSTR szFile, PSTR szPath)
{
    char    szBuf[MAXPATHLEN*2];

    if (szFile[1] == ':' && GetDiskPath(szFile[0],szBuf))
    {
        lstrcpy(szPath,szBuf);
        if (szFile[2])
            catpath(szPath,szFile + 2);
    }
    else
    {
        lstrcpy(szPath,szFile);
    }
    return TRUE;
}

/*----------------------------------------------------------------------------*\
|   wsCopy()                                                                   |
|                                                                              |
\*----------------------------------------------------------------------------*/
BOOL PUBLIC wsCopy(PSTR szSection)
{
    PINF    pinf;
    char    szSource[MAXSTR];
    char    szDest[MAXSTR];
    char    szLocalTmp[MAXSTR];
    int     nFiles;
    int	    err = ERROR_OK;
    char    buf[MAXSTR];

    // we use different sections for net and win386 setup.  net setup
    // overrides win386.

    dprintf("wsCopy %s\n",szSection);

    pinf = infFindSection(NULL,szSection);
    if (pinf == NULL)
        return FALSE;

    ProPrintf(ID_STATUS1, wsLoadSz(IDS_WAITCOPY,NULL));

    for (nFiles=0; pinf; pinf = infNextLine(pinf))
    {

    	dprintf("inf line:%ls\n", (LPSTR)pinf);

        infParseField(pinf,1,szSource);

        if (szSource[0] == '#') {

	    // count these, and copy below

	    nFiles += infLineCount(infFindSection(NULL, szSource + 1));
	    dprintf("nFiles:%d\n", nFiles);

        } else
            nFiles++;
    }
    dprintf("nFiles:%d\n", nFiles);

    ProSetBarRange(nFiles);

    for (pinf = infFindSection(NULL,szSection); pinf; pinf = infNextLine(pinf))
    {
        infParseField(pinf,1,szSource);
        infParseField(pinf,2,szDest);
        if ((err = FileCopy(szSource,szDest,(FPFNCOPY)wsCopyStatus,FC_FILE)) != ERROR_OK)
           break;
    }

    return err == ERROR_OK;
}

/*----------------------------------------------------------------------------*\
|   define the call back function used by the FileCopy() function.	       |
|                                                                              |
\*----------------------------------------------------------------------------*/

WORD FAR PASCAL wsCopyStatus(int msg, int n, LPSTR szFile)
{
    char buf[80];

    switch (msg)
    {
	case COPY_INSERTDISK:
            return wsInsertDisk(n,szFile);

	case COPY_ERROR:
            return wsCopyError(n,szFile);

	case COPY_QUERYCOPY:

	    // special case hack for .386 files built into win386.exe

	    infParseField(szFile, 1, buf);

	    if (*FileName(buf) == '*')
	    	return FALSE;		// don't copy

	    return TRUE;

	case COPY_END:
	case COPY_START:
	    SetErrorMode(msg == COPY_START);	// don't crit error on us
	    break;

        case COPY_STATUS:
	    if (n == 0)
	    {
	    	dprintf("%ls\n", (LPSTR)szFile);

		// if their is a title update it.  this allows shared titles

		if (infParseField(szFile,2,buf))
	                ProPrintf(ID_STATUS2, wsLoadSz(IDS_COPYING,NULL), (LPSTR)buf);
            }

	    if (n == 100)
	    {
		ProDeltaPos(1);
            }
            if (! wsYield() )
                return FC_ABORT;

	    break;
    }
    return FC_IGNORE;
}

/*----------------------------------------------------------------------------*\
|                                                                              |
| wsCopyError()                                                                |
|                                                                              |
|   Handles errors, as the result of copying files.                            |
|                                                                              |
|   this may include net contention errors, in witch case the user must	       |
|   retry the operation.						       |
|                                                                              |
\*----------------------------------------------------------------------------*/
WORD PUBLIC wsCopyError(int n, LPSTR sz)
{
    char buf[200];
    char file[MAXSTR];
    int  res;

    lstrcpy(file, sz);		// in case our DS moves durring wsLoadSz()

    if (!wsLoadSz(IDS_ERROR + n, buf)) {

    	// error string doesn't exist.  if it is a net error use
	// net string, if not that use generic error string

    	if (n > ERROR_SHARE)
		wsLoadSz(IDS_ERROR + ERROR_SHARE, buf);
	else
	        wsprintf(buf, wsLoadSz(IDS_ERROR,NULL), n);
    }

    lstrcat(buf,"\n");

    // check for the out of disk space case

    if (n == ERROR_WRITE) {		// check for out of disk space

    	dprintf("free space on disk %ld\n", DosDiskFreeSpace(0));

    	if (DosDiskFreeSpace(0) < 50000)
    		lstrcat(buf, wsLoadSz(IDS_OUTOFDISK, NULL));
    }

    lstrcat(buf, file);		// add the file name

    // pass these through globals

    pErrMsg = buf;
    res = fDialog(DLG_COPYERROR, GetActiveWindow(), wsErrorDlg);

    return res;
}


/*----------------------------------------------------------------------------*\
|                                                                              |
| wsInsertDisk()                                                               |
|                                                                              |
|   Handles errors, as the result of copying files.                            |
|                                                                              |
\*----------------------------------------------------------------------------*/
WORD PUBLIC wsInsertDisk(int n, LPSTR szSrcPath)
{
    cDisk  = (char)n;

    szEdit = szSrcPath;

    return (WORD)fDialog(DLG_INSERTDISK, GetActiveWindow(), wsDiskDlg);
}

/*----------------------------------------------------------------------------*\
|   wsDiskDlg( hDlg, uiMessage, wParam, lParam )                               |
|                                                                              |
|   Arguments:                                                                 |
|       hDlg            window handle of about dialog window                   |
|       uiMessage       message number                                         |
|       wParam          message-dependent                                      |
|       lParam          message-dependent                                      |
|                                                                              |
|   Returns:                                                                   |
|       TRUE if message has been processed, else FALSE                         |
|                                                                              |
\*----------------------------------------------------------------------------*/

BOOL FAR PASCAL wsDiskDlg(HWND hDlg, unsigned uiMessage, WORD wParam, long lParam)
{

    switch (uiMessage)
    {
	case WM_COMMAND:
	    switch (wParam)
	    {
		case ID_OK:

		    // szEdit points to the path that will be retried

		    GetDlgItemText(hDlg, ID_EDIT, szEdit, MAXSTR);
		    lstrcpy(szDiskPath, szEdit);	// and make this the default

                    EndDialog(hDlg, FC_RETRY);
		    break;

		case ID_CANCEL:

		    EndDialog(hDlg, FC_IGNORE);
		    break;
	    }
	    return TRUE;

        case WM_INITDIALOG:
	    {
            /*
             *  now look in the [disks] section for the disk name
             *  the disk name is the second field.
             */
	    char ach[2];
	    char buf[MAXSTR];
	    char buf2[MAXSTR];

            ach[0] = cDisk;
            ach[1] = 0;

            infGetProfileString(NULL,wsLoadSz(IDS_DISKS,NULL),ach,buf);

            infParseField(buf,2,buf2);

            SetDlgItemText(hDlg,ID_TEXT,buf2);
	    SetDlgItemText(hDlg,ID_EDIT,szEdit);

	    wsDlgInit(hDlg);

	    MessageBeep(0);

            return TRUE;
	    }
    }
    return FALSE;
}

/*----------------------------------------------------------------------------*\
|   wsYield()                                                                  |
|                                                                              |
|   Description:                                                               |
|       Handle any messages in our Que, return when the Que is empty           |
|                                                                              |
|   Returns:                                                                   |
|       FALSE if a WM_QUIT message was encountered, TRUE otherwise             |
|                                                                              |
\*----------------------------------------------------------------------------*/
BOOL PUBLIC wsYield()
{
    MSG     msg;
    BOOL    retval = TRUE;

    while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {

        if ( msg.message == WM_QUIT )
           retval = FALSE;

    	if (CheckSpecialKeys(&msg))
		continue;

        TranslateMessage(&msg);
        DispatchMessage(&msg);

    }
    return retval;
}

/***************************************************************************
 * BOOL PUBLIC CheckSpecialKeys(PMSG pmsg)
 * 
 * returns:
 *	TRUE	it was special and I delt with it
 *	FALSE	it wasn't special so pass it on
 *
 **************************************************************************/
BOOL PUBLIC CheckSpecialKeys(LPMSG lpmsg)
{
	static HWND hwndOldActive = NULL;

        if (lpmsg->message != WM_KEYDOWN)
		return FALSE;

        switch(lpmsg->wParam) {

	// exit keys

        case VK_F3:
		PostMessage(hwndWS,WM_COMMAND,ID_EXITSETUP,0L);
		break;

	default:
		return FALSE;
        }
	return TRUE;		// yes, this was a special key
}

#define CBSECTORSIZE	512
#define INT13_READ	2

/*--------------------------------------------------------------------------
									    
  IsValidDiskette() - 						    
									    
--------------------------------------------------------------------------*/

BOOL NEAR IsValidDiskette(int iDrive)
{
  char	    buf[CBSECTORSIZE];

  iDrive |= 0x0020;	// make lower case

  iDrive -= 'a';	// A = 0, B = 1, etc. for BIOS stuff

  return MyReadWriteSector(buf, INT13_READ, iDrive, 0, 0, 1);
}



#ifdef CHECK_FLOPPY

/*  BOOL IsDiskInDrive(char cDisk)
 *
 *  Is the specifed disk in the drive
 *
 *  ENTRY:
 *
 *  cDisk        : what disk required to be in the drive (logical)
 *
 *  return TRUE if the specifed disk is in the drive
 *         FALSE if the wrong disk is in the drive or disk error
 *
 */
BOOL NEAR IsDiskInDrive(int iDisk)
{

    if ((iDisk  >= 'A' && iDisk <= 'Z') || 
    	(iDisk  >= 'a' && iDisk <= 'z')) {

	    if (DosRemoveable(iDisk)) {

	        if (!IsValidDiskette(iDisk))
        		return FALSE;
	    }


	    return TRUE;
    }

    return TRUE;	// for non drive letters assume a path
    			// and thus always in.
}

#endif 

void FAR PASCAL catpath(PSTR path, PSTR sz)
{
    //
    // Remove any drive letters from the directory to append
    //
    if ( sz[1] == ':' )
       sz+=2;

    //
    // Remove any current directories ".\" from directory to append
    //
    while (sz[0] == '.' && SLASH(sz[1]))
        sz+=2;

    //
    // Dont append a NULL string or a single "."
    //
    if (*sz && !(sz[0] == '.' && sz[1] == 0))
    {
       if ( (!SLASH(path[lstrlen(path)-1])) && ((path[lstrlen(path)-1]) != ':') )
          lstrcat(path,CHSEPSTR);
       lstrcat(path,sz);
    }
}


PSTR FAR PASCAL FileName(PSTR szPath)
{
    PSTR   sz;

    for (sz=szPath; *sz; sz++)
        ;
    for (; sz>=szPath && !SLASH(*sz) && *sz!=':'; sz--)
        ;
    return ++sz;
}

PSTR FAR PASCAL StripPathName(PSTR szPath)
{
    PSTR   sz;

    sz = FileName(szPath);

    if (sz > szPath+1 && SLASH(sz[-1]) && sz[-2] != ':')
        sz--;

    *sz = 0;
    return szPath;
}
