#include "shellprv.h"
#pragma  hdrstop

#include "fstreex.h"

typedef struct {
    HWND hDlg;
    // input output
    LPTSTR   lpszExe;   // base file name (to search for)
    LPTSTR   lpszPath;  // starting location for search
    LPCTSTR  lpszName;  // doc type name "Winword Document"
} FINDEXE_PARAMS, *LPFINDEXE_PARAMS;


int CALLBACK LocateCallback(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData)
{
    TCHAR szPath[MAX_PATH + 80];
    int id;
    LPFINDEXE_PARAMS lpfind = (LPFINDEXE_PARAMS)lpData;

    switch(uMsg)
    {
        case BFFM_SELCHANGED:
            SHGetPathFromIDList((LPITEMIDLIST)lParam, szPath);
            if ((lstrlen(szPath) + lstrlen(lpfind->lpszExe)) <  MAX_PATH)
            {
                PathAppend(szPath, lpfind->lpszExe);
                if (PathFileExistsAndAttributes(szPath, NULL))
                {
                    id = IDS_FILEFOUND;
                }
                else
                {
                    id = IDS_FILENOTFOUND;
                }
            }
            else
            {
                id = IDS_FILENOTFOUND;
            }
            SendMessage(hwnd, BFFM_SETSTATUSTEXT, 0, id);
            break;

        case BFFM_INITIALIZED:
            SendMessage(hwnd, BFFM_SETSTATUSTEXT, 0, IDS_FILENOTFOUND);
            break;
    }
    return 0;
}

void _GetBrowseTitle(LPFINDEXE_PARAMS lpfind, LPTSTR lpszBuffer, UINT cchBuffer)
{
    TCHAR szTemplate[100];
    LoadString(HINST_THISDLL, IDS_FINDASSEXEBROWSETITLE, szTemplate, ARRAYSIZE(szTemplate));
    wnsprintf(lpszBuffer, cchBuffer, szTemplate, lpfind->lpszExe);
}

void DoBrowseForFile(LPFINDEXE_PARAMS lpfind)
{
    TCHAR szFilePath[MAX_PATH] = { 0 };       // buffer for file name
    TCHAR szInitialDir[MAX_PATH] = { 0 };       // buffer for file name

    // initial directory to Program Files
    SHGetSpecialFolderPath(NULL, szInitialDir, CSIDL_PROGRAM_FILES, TRUE); 

    if (GetFileNameFromBrowse(lpfind->hDlg, szFilePath, ARRAYSIZE(szFilePath), szInitialDir, 
            MAKEINTRESOURCE(IDS_EXE), MAKEINTRESOURCE(IDS_PROGRAMSFILTER), MAKEINTRESOURCE(IDS_BROWSE)))
    {
        SetDlgItemText(lpfind->hDlg, IDD_PATH, szFilePath);
        lstrcpy((LPTSTR)lpfind->lpszPath, szFilePath);
    }
}

void InitFindDlg(HWND hDlg, LPFINDEXE_PARAMS lpfind)
{
    TCHAR szPath[MAX_PATH]; /* This must be the same size as lpfind->lpszPath */
    TCHAR szBuffer[MAX_PATH + 100];

    SetWindowLongPtr(hDlg, DWLP_USER, (LONG_PTR)lpfind);
    lpfind->hDlg = hDlg;

    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);

    SHAutoComplete(GetDlgItem(hDlg, IDD_PATH), (SHACF_FILESYSTEM | SHACF_URLALL | SHACF_FILESYS_ONLY));
}

BOOL FindOk(LPFINDEXE_PARAMS lpfind)
{
    GetDlgItemText(lpfind->hDlg, IDD_PATH, lpfind->lpszPath, MAX_PATH);

    // If they entered a directory, then append the EXE name to it
    // The dialog is confusing - it asks for the "location".  Does that
    // mean I should enter the directory or the filename?
    // Allow both to work.
    if (PathIsDirectory(lpfind->lpszPath))
    {
        PathAppend(lpfind->lpszPath, lpfind->lpszExe);
    }

    if (!PathFileExistsAndAttributes(lpfind->lpszPath, NULL))
    {
        ShellMessageBox(HINST_THISDLL, lpfind->hDlg,
                          MAKEINTRESOURCE(IDS_STILLNOTFOUND), NULL, MB_ICONHAND | MB_OK, (LPTSTR)lpfind->lpszPath);
        return FALSE;
    }

    // HACKHACK we should use the registry but it's too late now;
    // Win95 shipped with this code so it's gonna be in win.ini forever...
    WriteProfileString(TEXT("programs"), lpfind->lpszExe, lpfind->lpszPath);
    return TRUE;
}

//----------------------------------------------------------------------------
// FindExeDlgProc was mistakenly exported in the original NT SHELL32.DLL when
// it didn't need to be (dlgproc's, like wndproc's don't need to be exported
// in the 32-bit world).  In order to maintain loadability of some app
// which might have linked to it, we stub it here.  If some app ended up really
// using it, then we'll look into a specific fix for that app.
//
// -BobDay
//
BOOL_PTR WINAPI FindExeDlgProc( HWND hDlg, UINT wMsg, WPARAM wParam, LONG lParam )
{
    return FALSE;
}

BOOL_PTR CALLBACK FindExeDlgProcA(HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam)
{
  LPFINDEXE_PARAMS lpfind = (LPFINDEXE_PARAMS)GetWindowLongPtr(hDlg, DWLP_USER);

  switch (wMsg)
    {
      case WM_INITDIALOG:
        InitFindDlg(hDlg, (LPFINDEXE_PARAMS)lParam);
        break;

      case WM_COMMAND:
        switch (GET_WM_COMMAND_ID(wParam, lParam))
          {
            case IDD_BROWSE:
              DoBrowseForFile(lpfind);
              break;

            case IDOK:
              if (!FindOk(lpfind))
                break;

              // fall through

            case IDCANCEL:
              EndDialog(hDlg, GET_WM_COMMAND_ID(wParam, lParam));
              break;

          }

        break;

      default:
        return FALSE;
    }

  return TRUE;
}

//
//  Give a command line, change the exe (leaving the args).  For example,
//  PathReplaceExe(C:\Old\foo.exe -silent, C:\NewDirectory\foo.exe)
//  yields C:\NewDirectory\foo.exe -silent.
//
void PathReplaceExe(LPTSTR lpCommand, UINT cchCommand, LPCTSTR pszExe)
{
    LPTSTR lpArgs;

    lpArgs = PathGetArgs(lpCommand);
    if (*lpArgs)
    {
        // Must save the original args before copying pszExe because it
        // might overwrite lpCommand.  I could be clever with hmemcpy and
        // avoid allocating memory but this function is called so rarely
        // it isn't worth it.
        UINT cchArgs = lstrlen(lpArgs);
        LPTSTR lpArgsCopy = (LPTSTR)_alloca((cchArgs + 1) * sizeof(TCHAR));
        lstrcpy(lpArgsCopy, lpArgs);

        lstrcpyn(lpCommand, pszExe, cchCommand);
        PathQuoteSpaces(lpCommand);
        // lpArgs is no good after this point

        lstrcatn(lpCommand, c_szSpace, cchCommand);
        lstrcatn(lpCommand, lpArgsCopy, cchCommand);
    }
    else
    {
        lstrcpyn(lpCommand, pszExe, cchCommand);
        PathQuoteSpaces(lpCommand);
    }
}

 //
 // put up cool ui to find the exe responsible for performing
 // a ShellExecute()
 // "excel.exe foo.xls" -> "c:\excel\excel.exe foo.xls"
 //
 // in:
 //     hwnd        to post UI on
 //     lpCommand   command line to try to repair [in/out]
 //     cchCommand  size of lpCommand buffer
 //     hkeyProgID  program ID
 //
 // out:
 //     lpCommand   change cmd line if we returned -1
 //
 // returns:
 //     -1  we found a new location lpCommand, use it
 //     or other win exec error codes, notably...
 //     2   we really didn't find it
 //     15  user cancel, fail the exec quietly
 //

int FindAssociatedExe(HWND hwnd, LPTSTR lpCommand, UINT cchCommand,
                                 LPCTSTR pszDocument, HKEY hkeyProgID)
{
    FINDEXE_PARAMS find;
    TCHAR szPath[MAX_PATH];
    TCHAR szExe[MAX_PATH];
    TCHAR szType[MAX_PATH];

    // strip down to just the EXE name
    lstrcpyn(szPath, lpCommand, ARRAYSIZE(szPath));
    PathRemoveArgs(szPath);
    PathUnquoteSpaces(szPath);

    // check to see if the file does exist. if it does then
    // the original exec must have failed because some
    // dependant DLL is missing.  so we return file not
    // found, even though we really found the file

    PathAddExtension(szPath, NULL);
    if (PathFileExists(szPath))
        return SE_ERR_FNF;          // file exists, but some dll must not

    // store the file name component
    lstrcpyn(szExe, PathFindFileName(szPath), ARRAYSIZE(szExe));

    // HACKHACK we should use the registry but it's too late now;
    // Win95 shipped with this code so it's gonna be in win.ini forever...

    GetProfileString(TEXT("programs"), szExe, szNULL, szPath, ARRAYSIZE(szPath));
    if (szPath[0]) {
        if (PathFileExists(szPath)) {
            PathReplaceExe(lpCommand, cchCommand, szPath); // return the new path
            return -1;                      // this means to try again
        }

        PathRemoveFileSpec(szPath);
    } else {
        /* Prompt with the disk that Windows is on */
        GetWindowsDirectory(szPath, ARRAYSIZE(szPath));
        szPath[3] = TEXT('\0');
    }

    SHGetTypeName(pszDocument, hkeyProgID, FALSE, szType, ARRAYSIZE(szType));

    find.lpszExe = szExe;       // base file name (to search for)
    find.lpszPath = szPath;     // starting location for search
    find.lpszName = szType;     // file type we are looking for

    switch (DialogBoxParam(HINST_THISDLL, MAKEINTRESOURCE(DLG_FINDEXE), hwnd, FindExeDlgProcA, (LPARAM)(LPFINDEXE_PARAMS)&find))
    {
    case IDOK:
        PathReplaceExe(lpCommand, cchCommand, szPath); // return the new path
        return -1;                  // file found and lpCommand fixed up

    case IDCANCEL:
        return ERROR_INVALID_FUNCTION; // This is the user cancel return

    default:
        return SE_ERR_FNF;             // stick with the file not found
    }
}