#define UNICODE 1

#include "shellprv.h"
#pragma  hdrstop

const WCHAR szCommdlgHelp[] = L"commdlg_help";

UINT wBrowseHelp = WM_USER; /* Set to an unused value */

const CHAR szGetOpenFileName[] = "GetOpenFileNameW";

/* the defines below should be in windows.h */

/* Dialog window class */
#define WC_DIALOG       (MAKEINTATOM(0x8002))

/* cbWndExtra bytes needed by dialog manager for dialog classes */
#define DLGWINDOWEXTRA  30


/* Get/SetWindowWord/Long offsets for use with WC_DIALOG windows */
#define DWL_MSGRESULT   0
#define DWL_DLGPROC     4
#define DWL_USER        8

/* For Long File Name support */
#define MAX_EXTENSION 64

typedef struct {
   LPWSTR lpszExe;
   LPWSTR lpszPath;
   LPWSTR lpszName;
} FINDEXE_PARAMS, FAR *LPFINDEXE_PARAMS;

typedef INT (APIENTRY *LPFNGETOPENFILENAME)(LPOPENFILENAME);

VOID APIENTRY
CheckEscapesW(LPWSTR szFile, DWORD cch)
{
   LPWSTR szT;
   WCHAR *p, *pT;

   for (p = szFile; *p; p++) {

       switch (*p) {
           case WCHAR_SPACE:
           case WCHAR_COMMA:
           case WCHAR_SEMICOLON:
           case WCHAR_HAT:
           case WCHAR_QUOTE:
           {
               // this path contains an annoying character
               if (cch < (wcslen(szFile) + 2)) {
                   return;
               }
               szT = (LPWSTR)LocalAlloc(LPTR, cch * sizeof(WCHAR));
               if (!szT) {
                   return;
               }
               wcscpy(szT,szFile);
               p = szFile;
               *p++ = WCHAR_QUOTE;
               for (pT = szT; *pT; ) {
                    *p++ = *pT++;
                }
                *p++ = WCHAR_QUOTE;
                *p = WCHAR_NULL;
                LocalFree(szT);
                return;
            }
        }
    }
}

VOID APIENTRY
CheckEscapesA(LPSTR lpFileA, DWORD cch)
{
   if (lpFileA && *lpFileA) {
      LPWSTR lpFileW;

      lpFileW = (LPWSTR)LocalAlloc(LPTR, (cch * sizeof(WCHAR)));
      if (!lpFileW) {
         return;
      }

      SHAnsiToUnicode(lpFileA, lpFileW, cch);

      CheckEscapesW(lpFileW, cch);

      try {
         SHUnicodeToAnsi(lpFileW, lpFileA, cch);
      } except(EXCEPTION_EXECUTE_HANDLER) {
         LocalFree(lpFileW);
         return;
      }

      LocalFree(lpFileW);
   }

   return;
}

// Hooks into common dialog to allow size of selected files to be displayed.
BOOL APIENTRY
LocateHookProc(
   HWND hDlg,
   UINT uiMessage,
   WPARAM wParam,
   LONG lParam)
{
   WCHAR szTemp[40];
   WORD wID;

   switch (uiMessage) {
   case WM_INITDIALOG:
           PostMessage(hDlg, WM_COMMAND, ctlLast+1, 0L);
           break;

   case WM_COMMAND:
      switch (GET_WM_COMMAND_ID(wParam, lParam)) {
         case ctlLast+1:
            GetDlgItemText(hDlg, edt1, szTemp, ARRAYSIZE(szTemp));
            if (SendDlgItemMessage(hDlg, lst1, LB_FINDSTRING, (WPARAM)-1,
                  (LONG_PTR)(LPSTR)szTemp) >= 0) {
               wID = IDS_PROGFOUND;
            } else {
               wID = IDS_PROGNOTFOUND;
            }
            LoadString(HINST_THISDLL, wID, szTemp, ARRAYSIZE(szTemp));
            SetDlgItemText(hDlg, ctlLast+2, szTemp);
            break;

         case lst2:
            if (GET_WM_COMMAND_CMD(wParam, lParam) == LBN_DBLCLK)
               PostMessage(hDlg, WM_COMMAND, ctlLast+1, 0L);
               break;

         case cmb2:
            switch (GET_WM_COMMAND_CMD(wParam, lParam)) {
               case CBN_SELCHANGE:
                   PostMessage(hDlg, WM_COMMAND, ctlLast+1, 1L);
                   break;

               case CBN_CLOSEUP:
                   PostMessage(hDlg, WM_COMMAND, GET_WM_COMMAND_MPS(cmb2,
                   GetDlgItem(hDlg, cmb2), CBN_SELCHANGE));
                   break;
                }
                break;

               case IDOK:
               case IDCANCEL:
               case IDABORT:
                  PostMessage(hDlg, WM_COMMAND, ctlLast+1, 0L);
                  break;
            }
            break;
   }
   UNREFERENCED_PARAMETER(lParam);
   return(FALSE);  // commdlg, do your thing
}

BOOL_PTR APIENTRY
FindExeDlgProcW(
   HWND hDlg,
   register UINT wMsg,
   WPARAM wParam,
   LPARAM lParam)
{
  /* Notice this is OK as a global, because it will be reloaded
   * when needed
   */
   static HANDLE hCommDlg = NULL;

   WCHAR szPath[MAX_PATH]; /* This must be the same size as lpfind->lpszPath */
   WCHAR szBuffer[MAX_PATH + 100];
   LPFINDEXE_PARAMS lpfind;
   int temp;
   LPWSTR lpTemp;

   switch (wMsg) {
      case WM_INITDIALOG:
         wBrowseHelp = RegisterWindowMessage(szCommdlgHelp);

         lpfind = (LPFINDEXE_PARAMS)lParam;

         SetWindowLongPtr(hDlg, DWLP_USER, (LONG_PTR)lpfind);

         GetDlgItemText(hDlg, IDD_TEXT1, szPath, ARRAYSIZE(szPath));
         wsprintf(szBuffer, szPath, lpfind->lpszExe, lpfind->lpszName);
         SetDlgItemText(hDlg, IDD_TEXT1, szBuffer);
         GetDlgItemText(hDlg, IDD_TEXT2, szPath, ARRAYSIZE(szPath));
         wsprintf(szBuffer, szPath, lpfind->lpszExe);
         SetDlgItemText(hDlg, IDD_TEXT2, szBuffer);

         SetDlgItemText(hDlg, IDD_PATH, lpfind->lpszPath);

         break;

      case WM_DESTROY:
         if (hCommDlg >= (HANDLE)32) {
            FreeLibrary(hCommDlg);
            hCommDlg = NULL;
         }
         break;

      case WM_COMMAND:
         switch (GET_WM_COMMAND_ID(wParam, lParam)) {
            case IDD_BROWSE:
               {
                  LPFNGETOPENFILENAME lpfnGetOpenFileName;
                  WCHAR szExts[MAX_EXTENSION];
                  OPENFILENAME ofn;

                  GetDlgItemText(hDlg, IDD_PATH, szBuffer, ARRAYSIZE(szBuffer));

                  lpfind = (LPFINDEXE_PARAMS)GetWindowLongPtr(hDlg, DWLP_USER);
                  wcscpy(szPath, lpfind->lpszExe);
                  SheRemoveQuotesW(szPath);

                  /* Make up a file types string
                  */
                  // BUG BUG this assumes extensions are of length 3.
                  szExts[0] = WCHAR_CAP_A;
                  szExts[1] = WCHAR_NULL;
                  szExts[2] = WCHAR_STAR;
                  szExts[3] = WCHAR_DOT;
                  szExts[4] = WCHAR_NULL;
                  if (NULL != (lpTemp=StrRChrW(szPath, NULL, WCHAR_DOT)))
                     StrCpyN(szExts+3, lpTemp, ((wcslen(lpTemp) < 60) ? wcslen(lpTemp) : 60));
                  szExts[3+wcslen(szExts+3)+1] = WCHAR_NULL;

                  ofn.lStructSize = sizeof(OPENFILENAME);
                  ofn.hwndOwner = hDlg;
                  ofn.hInstance = HINST_THISDLL;
                  ofn.lpstrFilter = L"A\0\?.?\0";   // a dummy filter
                  ofn.lpstrCustomFilter = NULL;
                  ofn.nMaxCustFilter = 0;
                  ofn.nFilterIndex = 1;
                  ofn.lpstrFile = szPath;
                  ofn.nMaxFile = sizeof(szPath);
                  ofn.lpstrInitialDir = NULL;
                  ofn.lpstrTitle = NULL;
                  ofn.Flags = OFN_HIDEREADONLY | OFN_FILEMUSTEXIST |
                  OFN_ENABLETEMPLATE | OFN_SHOWHELP;
                  ofn.lCustData = (LONG_PTR) hDlg;
                  ofn.lpfnHook = NULL;    // AddFileHookProc;
                  ofn.lpTemplateName = MAKEINTRESOURCE(DLG_BROWSE);
                  ofn.nFileOffset = 0;
                  ofn.nFileExtension = 0;
                  ofn.lpstrDefExt = NULL;
                  ofn.lpstrFileTitle = NULL;

                  if (hCommDlg < (HANDLE)32 &&
                     (hCommDlg = LoadLibrary(TEXT("comdlg32.dll"))) < (HANDLE)32) {
                        CommDlgError:
                        LoadString(HINST_THISDLL, IDS_NOCOMMDLG, szBuffer, ARRAYSIZE(szBuffer));
                        GetWindowText(hDlg, szPath, ARRAYSIZE(szPath));
                        MessageBox(hDlg, szBuffer, szPath, MB_ICONHAND|MB_OK);
                        break;
                     }
                  if (!(lpfnGetOpenFileName =
                     (LPFNGETOPENFILENAME)GetProcAddress((HINSTANCE)hCommDlg,
                     (LPSTR)szGetOpenFileName)))
                     goto CommDlgError;

                  temp = (*lpfnGetOpenFileName)(&ofn);

                  if (temp) {
                     LPWSTR lpTemp;

                     lpTemp = StrRChrW(szPath, NULL, WCHAR_BSLASH);
                     *lpTemp = WCHAR_NULL;
                     SetDlgItemText(hDlg, IDD_PATH, szPath);
                  }

                  break;
               }

            case IDOK:
               {
                  HANDLE hFile;

                  lpfind = (LPFINDEXE_PARAMS)GetWindowLongPtr(hDlg, DWLP_USER);
                  if (lpfind) {
                     GetDlgItemText(hDlg, IDD_PATH, lpfind->lpszPath, MAX_PATH);

                     switch (*CharPrev(lpfind->lpszPath,
                           lpTemp=lpfind->lpszPath+lstrlen(lpfind->lpszPath))) {
                        case WCHAR_BSLASH:
                        case WCHAR_COLON:
                           break;

                        default:
                           *lpTemp++ = WCHAR_BSLASH;
                           break;
                     }

                     wcscpy(lpTemp, lpfind->lpszExe);

                     hFile = CreateFile(lpfind->lpszPath, GENERIC_EXECUTE, (FILE_SHARE_READ | FILE_SHARE_WRITE),
                         NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

                     if (hFile == INVALID_HANDLE_VALUE) {
                        LoadString(HINST_THISDLL, IDS_STILLNOTFOUND, szPath, ARRAYSIZE(szPath));
                        wsprintf(szBuffer, szPath, lpfind->lpszPath);
                        GetWindowText(hDlg, szPath, ARRAYSIZE(szPath));
                        MessageBox(hDlg, szBuffer, szPath, MB_ICONHAND|MB_OK);
                        break;
                     }

                     WriteProfileString(TEXT("programs"), lpfind->lpszExe,
                        lpfind->lpszPath);
                  }
               }

            // fall through
            case IDCANCEL:
               EndDialog(hDlg, GET_WM_COMMAND_ID(wParam, lParam) == IDOK);
               break;
         }

         break;

      default:
         return FALSE;
   }

   return TRUE;
}


// Returns -1 if we found the file (and it was not in an obvious place)
// or an error code which will be returned to the app (see the error
// returns for ShellExecute)

HANDLE APIENTRY
FindAssociatedExeW(
   HWND hwnd,
   LPWSTR lpCommand,
   LPWSTR lpName)
{
   FINDEXE_PARAMS find;
   WCHAR szPath[MAX_PATH];
   WCHAR szExe[MAX_PATH];
   LPWSTR lpSpace, lpTemp;
   HANDLE hFile = NULL;
   BOOL fQuote = FALSE;


   // find the param list
   lpSpace = lpCommand;
   while (*lpSpace)
   {
       if ((*lpSpace == WCHAR_SPACE) && (!fQuote))
       {
           break;
       }
       else if (*lpSpace == WCHAR_QUOTE)
       {
           fQuote = !fQuote;
       }
       lpSpace++;
   }

   if (*lpSpace == WCHAR_SPACE) {
      *lpSpace = 0;
      wcscpy(szPath, lpCommand);
      *lpSpace = WCHAR_SPACE;
   } else {
      lpSpace = TEXT("");
      wcscpy(szPath, lpCommand);
   }
   SheRemoveQuotesW(szPath);

   /* Add ".exe" if there is no extension
    * Check if the file can be opened; if it can, then some DLL could not
    * be found during the WinExec, so return file not found
    */
   if (NULL != (lpTemp=StrRChrW(szPath, NULL, WCHAR_BSLASH))
       || NULL != (lpTemp=StrRChrW(szPath, NULL, WCHAR_COLON))) {
      ++lpTemp;
   } else {
      lpTemp = szPath;
   }

   hFile = CreateFile(szPath, GENERIC_EXECUTE, (FILE_SHARE_READ | FILE_SHARE_WRITE),
       NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

   if (hFile != INVALID_HANDLE_VALUE) {
      CloseHandle(hFile);
      return((HANDLE)2);
   }

   // store the file name component
   wcscpy(szExe, lpTemp);

   // make sure there is an extension
   if (!StrChrW(szExe, WCHAR_DOT)) {
      wcscat(szExe, TEXT(".exe"));
   }

   // add back the quotes, if necessary
   CheckEscapesW(szExe, MAX_PATH);

   // look in win.ini
   GetProfileString(TEXT("programs"), szExe, TEXT(""), szPath, ARRAYSIZE(szPath));

   if (szPath[0]) {
      hFile = CreateFile(szPath, GENERIC_EXECUTE, (FILE_SHARE_READ | FILE_SHARE_WRITE),
          NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

      if (hFile != INVALID_HANDLE_VALUE) {

         CloseHandle(hFile);
         wcscat(szPath, lpSpace);       // add the parameters
         wcscpy(lpCommand, szPath);     // return the new path
         return((HANDLE)-1);
      }

      /* Strip off the file part */
      if (NULL != (lpTemp=StrRChrW(szPath, NULL, WCHAR_BSLASH))) {
         if (*CharPrev(szPath, lpTemp) == WCHAR_COLON) {
            ++lpTemp;
         }
         *lpTemp = WCHAR_NULL;
      } else if (NULL != (lpTemp=StrRChrW(szPath, NULL, WCHAR_COLON))) {
         *(lpTemp+1) = WCHAR_NULL;
      }
   } else {
      /* Prompt with the disk that Windows is on */
      GetWindowsDirectory(szPath, ARRAYSIZE(szPath)-1);
      szPath[3] = WCHAR_NULL;
   }

   find.lpszExe = szExe;
   find.lpszPath = szPath;
   find.lpszName = lpName;

   switch(DialogBoxParam(HINST_THISDLL, MAKEINTRESOURCE(DLG_FINDEXE), hwnd,
         FindExeDlgProcW, (LONG_PTR)(LPFINDEXE_PARAMS)&find)) {
      case IDOK:
          wcscat(szPath, lpSpace);      // add the parameters
          wcscpy(lpCommand, szPath);    // return the new path
          return ((HANDLE)-1);

      case IDCANCEL:
          return((HANDLE)15);                   // This is the user cancel return

      default:
          return((HANDLE)2);                    // stick with the file not found
   }
}
