1 /*
2  * w32oo:  collection of Win32-specific functions that require a C++ (OO)
3  *         compilation.
4  *
5  * Caveats
6  * =======
7  * - Due to a conflict between estruct.h and MS names, the Windows macros
8  *   "FAILED" may not be used to test an OLE return code.  Use SUCCEEDED
9  *   instead.
10  *
11  * $Id: w32oo.cpp,v 1.21 2018/11/05 01:29:09 tom Exp $
12  */
13 
14 #include "w32vile.h"
15 
16 #include <ole2.h>
17 #include <shlobj.h>
18 #include <stdio.h>
19 
20 #ifdef __cplusplus
21 extern "C" {
22 #endif
23 
24 #include "estruct.h"
25 #include "edef.h"
26 #include "dirstuff.h"
27 
28 #if CC_TURBO
29 #include <dir.h>                /* for 'chdir()' */
30 #endif
31 
32 #ifdef UNICODE
33 static int
vl_GetPathFromIDList(LPITEMIDLIST lp,char * bufferp)34 vl_GetPathFromIDList(LPITEMIDLIST lp, char *bufferp)
35 {
36     int rc = 0;
37     W32_CHAR buffer[FILENAME_MAX];
38     char *result;
39 
40     rc = SHGetPathFromIDList(lp, reinterpret_cast<LPTSTR>(buffer));
41     if (rc) {
42         if ((result = asc_charstring(buffer)) != 0) {
43             strcpy(bufferp, result);
44             free (result);
45         }
46     }
47     return rc;
48 }
49 #else
50 #define vl_GetPathFromIDList(listp, bufferp) SHGetPathFromIDList(listp, bufferp)
51 #endif
52 
53 /* ---------------------------- Favorites --------------------------------- */
54 
55 /*
56  * Return path to favorites folder.  don't know of any other way to do this
57  * than via COM ... a bit over the top, doncha' think?
58  */
59 const char *
get_favorites(void)60 get_favorites(void)
61 {
62     char         dir[FILENAME_MAX];
63     static       char *path;
64     LPITEMIDLIST pidl;
65     HRESULT      hr;
66 
67     if (! path)
68     {
69 #ifndef VILE_OLE
70         hr = OleInitialize(NULL);
71         if (! SUCCEEDED(hr))
72         {
73             disp_win32_error(hr, NULL);
74             return (NULL);
75         }
76 #endif
77         pidl = NULL;
78         hr   = SHGetSpecialFolderLocation(NULL, CSIDL_FAVORITES, &pidl);
79         if (! SUCCEEDED(hr))
80         {
81             disp_win32_error(hr, NULL);
82             CoTaskMemFree(pidl);     // API accepts NULL ptr
83 #ifndef VILE_OLE
84             OleUninitialize();
85 #endif
86             return (NULL);
87         }
88 
89         if (! vl_GetPathFromIDList(pidl, dir)) {
90             disp_win32_error(W32_SYS_ERROR, NULL);
91         } else {
92             bsl_to_sl_inplace(dir);  // Convert to canonical form
93             path = strmalloc(dir);
94             if (! path)
95                 no_memory("get_favorites()");
96         }
97         CoTaskMemFree(pidl);
98 #ifndef VILE_OLE
99         OleUninitialize();
100 #endif
101     }
102     return (path);
103 }
104 
105 /* --------------------------- Graphical CD ------------------------------- */
106 
107 static const char *initial_browse_dir;
108 
109 /*
110  * Setting the initial browse folder for SHBrowseForFolder() is, *cough*,
111  * insane.  Oh by the way, if a pidl isn't used to set the initial browse
112  * folder, then initial paths like "\\some_unc_path" don't work.  I kid
113  * you not.
114  *
115  * Much of this callback's code is cribbed from the 'Net.
116  */
117 static int CALLBACK
BrowseCallbackProc(HWND hwnd,UINT uMsg,LPARAM lp,LPARAM pData)118 BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lp, LPARAM pData)
119 {
120     HRESULT       hr;
121     ULONG         len;
122     LPITEMIDLIST  pidl;
123     LPSHELLFOLDER pShellFolder;
124     W32_CHAR      szDir[MAX_PATH];
125     LPWSTR        wide_path;
126 
127     switch(uMsg)
128     {
129         case BFFM_INITIALIZED:
130             // Set/initial the browse folder path.  This code is a hoot.
131             len = MultiByteToWideChar(CP_ACP,
132                                       MB_USEGLYPHCHARS|MB_PRECOMPOSED,
133                                       initial_browse_dir,
134                                       -1,
135                                       0,
136                                       0);
137             if (len == 0)
138             {
139                 disp_win32_error(GetLastError(), hwnd);
140                 break;
141             }
142             wide_path = new WCHAR[len];
143             MultiByteToWideChar(CP_ACP,
144                                 MB_USEGLYPHCHARS|MB_PRECOMPOSED,
145                                 initial_browse_dir,
146                                 -1,
147                                 wide_path,
148                                 len);
149             pShellFolder = 0;
150             hr = SHGetDesktopFolder(&pShellFolder);
151             if (! SUCCEEDED(hr))
152             {
153                 delete [] wide_path;
154                 disp_win32_error(hr, hwnd);
155                 break;
156             }
157             hr = pShellFolder->ParseDisplayName(0,
158                                                 0,
159                                                 wide_path,
160                                                 NULL,
161                                                 &pidl,
162                                                 0);
163             if (! SUCCEEDED(hr)) {
164                 disp_win32_error(hr, hwnd);
165             } else {
166                 SendMessage(hwnd, BFFM_SETSELECTION, FALSE, (LPARAM) pidl);
167                 CoTaskMemFree(pidl);
168             }
169             delete [] wide_path;
170             pShellFolder->Release();
171             break;
172         case BFFM_SELCHANGED:
173            // Set the status window text to the currently selected path.
174            if (SHGetPathFromIDList((LPITEMIDLIST) lp, reinterpret_cast<LPTSTR>(szDir)))
175               SendMessage(hwnd, BFFM_SETSTATUSTEXT, 0, (LPARAM) szDir);
176            break;
177         default:
178            break;
179     }
180     return (0);
181 }
182 
183 static int
graphical_cd(const char * dir)184 graphical_cd(const char *dir)
185 {
186     BROWSEINFO   bi;
187     HWND         hwnd;
188     LPITEMIDLIST pidl;
189     int          rc = FALSE;
190     char         bslbuf[FILENAME_MAX + 1], selected_folder[FILENAME_MAX + 1];
191 
192     TRACE((T_CALLED "graphical_cd(%s)\n", dir));
193 
194     // win32 functions don't like '/' as path delimiter.
195     if (! w32_glob_and_validate_dir(dir, bslbuf))
196         returnCode(rc);
197 
198     // pShellFolder->ParseDisplayName() will not accept local system folder
199     // names that don't include a drive letter.  Yes, this is fatal brain
200     // damage, but it's not like there are other supported, non-deprecated
201     // alternatives.  Fortunately, lengthen_path() rides to the rescue.
202     // Unfortunately, lengthen_path() reverts to '/' as a path delimiter.
203     // Oh, what tangled webs we weave.
204     initial_browse_dir = sl_to_bsl(lengthen_path(bslbuf));
205     TRACE(("initial_browse_dir=%s\n", initial_browse_dir));
206 
207 #if DISP_NTWIN
208     hwnd = (HWND) winvile_hwnd();
209 #else
210     hwnd = GetVileWindow();
211 #endif
212 
213 #ifndef VILE_OLE
214     HRESULT hr = OleInitialize(NULL);
215     if (! SUCCEEDED(hr))
216     {
217         disp_win32_error(hr, hwnd);
218         returnCode(rc);
219     }
220 #endif
221     memset(&bi, 0, sizeof(bi));
222     bi.hwndOwner = hwnd;
223     bi.ulFlags   = BIF_STATUSTEXT;
224     bi.lpfn      = BrowseCallbackProc;
225     if ((pidl = SHBrowseForFolder(&bi)) != NULL)
226     {
227         if (! vl_GetPathFromIDList(pidl, selected_folder))
228             disp_win32_error(NOERROR, hwnd);
229         else
230             rc = set_directory(selected_folder);
231         CoTaskMemFree(pidl);
232     }
233     // else -- user cancel'd out of dialog box
234 
235 #ifndef VILE_OLE
236     OleUninitialize();
237 #endif
238     returnCode(rc);
239 }
240 
241 int
wincd(int f,int n)242 wincd(int f, int n)
243 {
244     char         buf[NFILEN];
245     static TBUFF *last;
246     int          rc;
247 
248     (void) f;
249     (void) n;
250 
251     TRACE((T_CALLED "wincd(%d,%d)\n", f, n));
252     buf[0] = '\0';
253     rc     = mlreply_dir("Initial Directory: ", &last, buf);
254     if (rc == TRUE)
255     {
256         mktrimmed(buf);
257         rc = graphical_cd(buf);
258     }
259     else if (rc == FALSE)
260     {
261         /* empty response */
262 
263         if (getcwd(buf, sizeof(buf)) == NULL) {
264             mlforce("[getcwd: %s]", strerror(errno));
265             return FALSE;
266         }
267         rc = graphical_cd(buf);
268     }
269     /* else rc == ABORT or SORTOFTRUE */
270     returnCode(rc);
271 }
272 
273 /* explicitly specify initial directory */
274 int
wincd_dir(const char * dir)275 wincd_dir(const char *dir)
276 {
277     char buf[NFILEN];
278 
279     TRACE((T_CALLED "wincd_dir(%s)\n", dir));
280     if (dir == NULL)
281     {
282         if ((dir = getcwd(buf, sizeof(buf))) == NULL) {
283             mlforce("[getcwd: %s]", strerror(errno));
284             return FALSE;
285         }
286     }
287     returnCode(graphical_cd(dir));
288 }
289 
290 #if CC_MINGW
291 #define USE_EXCEPTION_FILTERING 0
292 #elif defined(EXCEPTION_INVALID_HANDLE)
293 #define USE_EXCEPTION_FILTERING 1
294 #elif defined(STATUS_INVALID_HANDLE)
295 #define EXCEPTION_INVALID_HANDLE STATUS_INVALID_HANDLE
296 #define USE_EXCEPTION_FILTERING 1
297 #else
298 #define USE_EXCEPTION_FILTERING 0
299 #endif
300 
301 #if USE_EXCEPTION_FILTERING
302 static int
filterExceptions(unsigned int code,struct _EXCEPTION_POINTERS * ep)303 filterExceptions(unsigned int code, struct _EXCEPTION_POINTERS *ep)
304 {
305     if (code == EXCEPTION_INVALID_HANDLE)
306     {
307         return EXCEPTION_EXECUTE_HANDLER;
308     }
309     else
310     {
311         return EXCEPTION_CONTINUE_SEARCH;
312     }
313 }
314 #endif
315 
316 // filter out STATUS_INVALID_HANDLE aka EXCEPTION_INVALID_HANDLE
317 void
w32_close_handle(HANDLE handle)318 w32_close_handle(HANDLE handle)
319 {
320 #if USE_EXCEPTION_FILTERING
321     __try
322     {
323         (void) CloseHandle(handle);
324     }
325     __except(filterExceptions(GetExceptionCode(), GetExceptionInformation()))
326     {
327         TRACE(("error closing handle %#x\n", handle));
328     }
329 #else
330     (void) CloseHandle(handle);
331 #endif
332 }
333 
334 #ifdef __cplusplus
335 } /* Extern "C" */
336 #endif
337