1 /*
2 * vile:notabinsert sw=4:
3 *
4 * wvwrap.cpp: A WinVile WRAPper .
5 *
6 * Originally written by Ed Henderson.
7 *
8 * This wrapper may be used to open one or more files via a right mouse
9 * click in the Windows Explorer. For more details, please read
10 * doc/oleauto.doc .
11 *
12 * Note: A great deal of the code included in this file is copied
13 * (almost verbatim) from other vile modules.
14 *
15 * $Id: wvwrap.cpp,v 1.23 2016/07/27 09:28:11 tom Exp $
16 */
17
18 #include "w32vile.h"
19
20 #include <objbase.h>
21 #include <stdio.h>
22 #include <string.h>
23 #include <ctype.h>
24
25 #include <initguid.h>
26
27 #include "w32reg.h"
28 #include "w32ole.h"
29 #include "vl_alloc.h"
30 #include "makeargv.h"
31
32 #define DQUOTE '"'
33 #define SQUOTE '\''
34
35 static size_t olebuf_len; /* scaled in wchar_t */
36 static OLECHAR *olebuf;
37
38 #ifdef OPT_TRACE
39 #define Trace MyTrace
40 static void
Trace(const char * fmt,...)41 Trace(const char *fmt,...)
42 {
43 FILE *fp = fopen("c:\\temp\\wvwrap.log", "a");
44 if (fp != 0) {
45 va_list ap;
46 va_start(ap, fmt);
47 vfprintf(fp, fmt, ap);
48 va_end(ap);
49 fclose(fp);
50 }
51 }
52
53 #define TRACE(params) Trace params
54 #else
55 #define OPT_TRACE 0
56 #define TRACE(params) /* nothing */
57 #endif
58
59 //--------------------------------------------------------------
60
61 /* WINVER >= _0x0500 */
62 #ifndef WC_NO_BEST_FIT_CHARS
63 #define WC_NO_BEST_FIT_CHARS 0
64 #endif
65
66 static char *
asc_charstring(const LPTSTR source)67 asc_charstring(const LPTSTR source)
68 {
69 char *target = 0;
70
71 if (source != 0) {
72 #if (defined(_UNICODE) || defined(UNICODE))
73 ULONG len = WideCharToMultiByte(CP_ACP,
74 WC_NO_BEST_FIT_CHARS,
75 source,
76 -1,
77 0,
78 0,
79 NULL,
80 NULL);
81 if (len) {
82 target = typecallocn(char, len + 1);
83
84 (void) WideCharToMultiByte(CP_ACP,
85 WC_NO_BEST_FIT_CHARS,
86 source,
87 -1,
88 target,
89 len,
90 NULL,
91 NULL);
92 }
93 #else
94 unsigned len = strlen(source) + 1;
95 target = typecallocn(char, len + 1);
96 if (target != 0)
97 memcpy(target, source, len);
98 #endif
99 }
100
101 return target;
102 }
103 static LPTSTR
w32_charstring(const char * source)104 w32_charstring(const char *source)
105 {
106 TCHAR *target = 0;
107
108 if (source != 0) {
109 #if (defined(_UNICODE) || defined(UNICODE))
110 ULONG len = MultiByteToWideChar(CP_ACP,
111 MB_USEGLYPHCHARS | MB_PRECOMPOSED,
112 source,
113 -1,
114 0,
115 0);
116 if (len != 0) {
117 target = typecallocn(TCHAR, len + 1);
118
119 (void) MultiByteToWideChar(CP_ACP,
120 MB_USEGLYPHCHARS | MB_PRECOMPOSED,
121 source,
122 -1,
123 target,
124 len);
125 }
126 #else
127 unsigned len = strlen(source) + 1;
128 target = typecallocn(TCHAR, len + 1);
129 if (target != 0)
130 memcpy(target, source, len);
131 #endif
132 }
133
134 return target;
135 }
136
137 /* from w32misc.c */
138 static LPTSTR
w32_prognam(void)139 w32_prognam(void)
140 {
141 return W32_STRING("wvwrap");
142 }
143
144 int
w32_message_box(HWND hwnd,const char * message,int code)145 w32_message_box(HWND hwnd, const char *message, int code)
146 {
147 int rc;
148 LPTSTR buf = w32_charstring(message);
149
150 rc = MessageBox(hwnd, buf, w32_prognam(), code);
151 free((void *) buf);
152 return (rc);
153 }
154
155 static char *
fmt_win32_error(ULONG errcode,char ** buf,ULONG buflen)156 fmt_win32_error(ULONG errcode, char **buf, ULONG buflen)
157 {
158 int flags = FORMAT_MESSAGE_FROM_SYSTEM;
159 LPTSTR result = 0;
160
161 if (*buf) {
162 result = typeallocn(TCHAR, buflen + 1);
163 } else {
164 flags |= FORMAT_MESSAGE_ALLOCATE_BUFFER;
165 }
166
167 FormatMessage(flags,
168 NULL,
169 errcode,
170 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* dflt language */
171 result,
172 buflen,
173 NULL);
174
175 if (*buf != 0) {
176 char *tmp = asc_charstring(result);
177 strcpy(*buf, tmp);
178 free(tmp);
179 }
180 return (*buf);
181 }
182
183 static int
nomem(void)184 nomem(void)
185 {
186 char buf[512], *tmp;
187
188 tmp = buf;
189 fmt_win32_error((ULONG) E_OUTOFMEMORY, &tmp, 0);
190 w32_message_box(NULL, buf, MB_OK | MB_ICONSTOP);
191 return (1);
192 }
193
194 #if !(defined(_UNICODE) || defined(UNICODE))
195 /*
196 * Quick & Dirty Unicode conversion routine. Routine uses a
197 * dynamic buffer to hold the converted string so it may be any arbitrary
198 * size. However, the same dynamic buffer is reused when the routine is
199 * called a second time. So make sure that the converted string is
200 * used/copied before the conversion routine is called again.
201 *
202 * from w32ole.cpp
203 */
204 static OLECHAR *
ConvertToUnicode(const char * szA)205 ConvertToUnicode(const char *szA)
206 {
207 size_t len;
208
209 len = strlen(szA) + 1;
210 if (len > olebuf_len) {
211 if (olebuf)
212 free(olebuf);
213 olebuf_len = olebuf_len * 2 + len;
214 if ((olebuf = typeallocn(OLECHAR, olebuf_len)) == NULL)
215 return (olebuf); /* We're gonna' die */
216 }
217 mbstowcs(olebuf, szA, len);
218 return (olebuf);
219 }
220 #endif
221
222 /*
223 * vile's prompts for directory/filename do not expect to strip quotes; they
224 * use the strings exactly as given. At the same time, the initial "cd" and
225 * "e" commands use the token parsing which stops on a blank. That makes it
226 * not simple to use OLE to send characters to the server to specify filenames
227 * containing blanks.
228 *
229 * We work around the problem using the "eval" command, which forces a reparse
230 * of the whole line. That uses an extra level of interpretation, so we have
231 * to double each single quote _twice_ to enter single quotes in the filename.
232 *
233 * We turn off globbing to prevent vile from expanding dollar-signs and square
234 * brackets.
235 *
236 * Obscure, but it works.
237 */
238 static char *
escape_quotes(const char * src)239 escape_quotes(const char *src)
240 {
241 size_t len = 4 * strlen(src) + 1;
242 char *result = typeallocn(char, len);
243
244 if (result == 0)
245 exit(nomem());
246
247 char *dst = result;
248 while (*src != '\0') {
249 UCHAR ch = (UCHAR) * src;
250 // only send ASCII, do not rely on the runtime to guess how to handle
251 // non-ASCII characters.
252 if (ch > 127) {
253 sprintf(dst, "\026x%02x", ch);
254 dst += 4;
255 src += 1;
256 continue;
257 } else {
258 if (*src == SQUOTE) {
259 *dst++ = SQUOTE;
260 *dst++ = SQUOTE;
261 *dst++ = SQUOTE;
262 }
263 *dst++ = *src++;
264 }
265 }
266 *dst = '\0';
267
268 return result;
269 }
270
271 static void
append(char * & buffer,size_t & length,char * value)272 append(char *&buffer, size_t &length, char *value)
273 {
274 size_t newsize = strlen(buffer) + strlen(value);
275 if ((newsize + 1) > length) {
276 length = (newsize + 1) * 2;
277 buffer = (char *) realloc(buffer, length);
278 }
279 strcat(buffer, value);
280 }
281
282 #define MY_BUFSIZ 4096
283
284 int WINAPI
WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow)285 WinMain(HINSTANCE hInstance, // handle to current instance
286 HINSTANCE hPrevInstance, // handle to previous instance
287 LPSTR lpCmdLine, // pointer to command line
288 int nCmdShow) // show state of window
289 {
290 BSTR bstr;
291 size_t dynbuf_len;
292 HRESULT hr;
293 HWND hwnd;
294 VARIANT_BOOL insert_mode, glob_mode, minimized;
295 char *lclbuf = NULL, tmp[512], *dynbuf;
296 OLECHAR *olestr;
297 LPUNKNOWN punk;
298 IVileAuto *pVileAuto;
299 char **argv;
300 int argc;
301 int argc1;
302
303 if (make_argv(0, lpCmdLine, &argv, &argc, NULL) < 0)
304 return (nomem());
305 argc1 = after_options(0, argc, argv);
306
307 #if OPT_TRACE
308 Trace("; cmdline:%s\n", lpCmdLine);
309 for (int n = 0; n < argc; ++n)
310 Trace("; %sargv%d:%s\n", (n >= argc1) ? "*" : "", n, argv[n]);
311 #endif
312
313 olebuf_len = MY_BUFSIZ;
314 dynbuf_len = MY_BUFSIZ;
315 olebuf = typeallocn(OLECHAR, olebuf_len);
316 dynbuf = typeallocn(char, dynbuf_len);
317 if (!(olebuf && dynbuf))
318 return (nomem());
319
320 hr = CoInitialize(NULL); // Fire up COM.
321 if (FAILED(hr)) {
322 fmt_win32_error(hr, &lclbuf, 0);
323 sprintf(tmp,
324 "ERROR: CoInitialize() failed, errcode: %#lx, desc: %s",
325 hr,
326 lclbuf);
327 w32_message_box(NULL, tmp, MB_OK | MB_ICONSTOP);
328 return (1);
329 }
330 hr = CoCreateInstance(CLSID_VileAuto,
331 NULL,
332 CLSCTX_LOCAL_SERVER,
333 IID_IUnknown,
334 (void **) &punk);
335 if (FAILED(hr)) {
336 fmt_win32_error(hr, &lclbuf, 0);
337 sprintf(tmp,
338 "ERROR: CoInitialize() failed, errcode: %#lx, desc: %s\n"
339 "Try registering winvile via its -Or switch.",
340 hr,
341 lclbuf);
342 w32_message_box(NULL, tmp, MB_OK | MB_ICONSTOP);
343 return (1);
344 }
345 hr = punk->QueryInterface(IID_IVileAuto, (void **) &pVileAuto);
346 punk->Release();
347 if (FAILED(hr)) {
348 fmt_win32_error(hr, &lclbuf, 0);
349 sprintf(tmp,
350 "ERROR: QueryInterface() failed, errcode: %#lx, desc: %s\n\n",
351 hr,
352 lclbuf);
353 w32_message_box(NULL, tmp, MB_OK | MB_ICONSTOP);
354 return (1);
355 }
356 pVileAuto->put_Visible(VARIANT_TRUE); // May not be necessary
357 pVileAuto->get_InsertMode(&insert_mode);
358 pVileAuto->get_GlobMode(&glob_mode);
359 if (argc > argc1) {
360 char *cp;
361
362 *dynbuf = '\0';
363
364 if (insert_mode)
365 append(dynbuf, dynbuf_len, "\033");
366
367 #if OPT_TRACE
368 /*
369 * Turn on tracing in the server, for debugging.
370 */
371 append(dynbuf, dynbuf_len, ":setv $debug=true\n");
372 #endif
373 /*
374 * Disable globbing to simplify quoting.
375 */
376 append(dynbuf, dynbuf_len, ":set noglob\n");
377
378 /*
379 * When wvwrap starts up (and subsequently launches winvile), the
380 * editor's CWD is set to a path deep within the bowels of Windows.
381 * Not very useful. Change CWD to the directory of the first
382 * file on the commandline.
383 */
384 cp = strrchr(*argv, '\\');
385 if (cp) {
386 int add_delim;
387
388 *cp = '\0';
389 if (cp == *argv) {
390 /* filename is of the form: \<leaf> . handle this. */
391 append(dynbuf, dynbuf_len, ":cd \\\n");
392 } else {
393 /*
394 * To add insult to injury, Windows Explorer has a habit of
395 * passing 8.3 filenames to wvwrap (noted on a win2K system and
396 * a FAT32 partition). If the folder portion of the file's
397 * path happens to be in 8.3 format (i.e., a tilde included in
398 * the folder name), then 8.3 folder names appear in winvile's
399 * Recent Folders list.
400 *
401 * Needless to say, it's no fun trying to decipher 8.3 folder
402 * names.
403 */
404
405 LPTSTR the_arg = w32_charstring(*argv);
406 TCHAR folder[FILENAME_MAX];
407 char *fp;
408
409 if (GetLongPathName(the_arg, folder, sizeof(folder)) > 0)
410 fp = asc_charstring(folder);
411 else
412 fp = asc_charstring(the_arg);
413
414 add_delim = (isalpha(fp[0]) && fp[1] == ':' && fp[2] == '\0');
415
416 /*
417 * With regard to the following code, note that the
418 * original file might be in the form "<drive>:\leaf", in
419 * which case *argv now points at "<drive>:" . Recall that
420 * cd'ing to <drive>: on a DOS/WIN32 host has special
421 * semantics (which we don't want).
422 */
423 char temp[MY_BUFSIZ];
424 sprintf(temp, ":eval cd '%s%s'\n",
425 escape_quotes(fp),
426 (add_delim) ? "\\" : "");
427
428 append(dynbuf, dynbuf_len, temp);
429 free(fp);
430 free(the_arg);
431 }
432 *cp = '\\';
433 }
434
435 while (argc1 < argc) {
436 char temp[MY_BUFSIZ];
437 sprintf(temp, ":eval e '%s'\n", escape_quotes(argv[argc1++]));
438 append(dynbuf, dynbuf_len, temp);
439 }
440 if (glob_mode)
441 append(dynbuf, dynbuf_len, ":set glob\n");
442
443 TRACE(("; dynbuf:\n%s\n", dynbuf));
444 olestr = TO_OLE_STRING(dynbuf);
445 bstr = SysAllocString(olestr);
446
447 if (!(bstr && olestr))
448 return (nomem());
449 pVileAuto->VileKeys(bstr);
450
451 SysFreeString(bstr);
452 free(olestr);
453 }
454
455 // Set foreground window using a method that's compatible with win2k
456 pVileAuto->get_MainHwnd((LONG *) & hwnd);
457 (void) SetForegroundWindow(hwnd);
458
459 // If editor minimized, restore window
460 hr = pVileAuto->get_IsMinimized(&minimized);
461 if (SUCCEEDED(hr) && minimized)
462 hr = pVileAuto->Restore();
463 pVileAuto->Release();
464 CoUninitialize(); // shut down COM
465 return (0);
466 }
467